@mastra/auth-neon 0.0.0 → 0.2.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/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,373 @@
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;
10
+ 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
+ }
103
+ };
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
+ }
112
+ function stripTrailingSlashes(url) {
113
+ let end = url.length;
114
+ while (end > 0 && url[end - 1] === "/") {
115
+ end--;
116
+ }
117
+ return url.slice(0, end);
118
+ }
119
+ 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+=)/);
126
+ }
127
+ 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
+ };
139
+ }
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
+ }
367
+ };
368
+
369
+ exports.MastraAuthNeon = MastraAuthNeon;
370
+ exports.MastraRBACNeon = MastraRBACNeon;
371
+ exports.mapNeonUserToEEUser = mapNeonUserToEEUser;
372
+ //# sourceMappingURL=index.cjs.map
373
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rbac-provider.ts","../src/index.ts"],"names":["resolvePermissionsFromMapping","matchesPermission","MastraAuthProvider","createRemoteJWKSet","jwtVerify"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Neon Auth RBAC provider for Mastra.\n *\n * Maps Neon Auth organization roles (via Better Auth's Organization plugin)\n * to Mastra permissions using a configurable role mapping.\n */\n\nimport type { IRBACProvider, RoleMapping } from '@mastra/core/auth/ee';\nimport { resolvePermissionsFromMapping, matchesPermission } from '@mastra/core/auth/ee';\n\nimport type { NeonAuthUser } from './index';\n\n/**\n * Response shape from the Neon Auth organization member endpoint.\n */\ninterface NeonOrgMember {\n id: string;\n organizationId: string;\n userId: string;\n role: string;\n createdAt: string;\n}\n\nexport interface NeonRoleMappingOptions {\n /** Cache TTL in milliseconds (default: 60_000) */\n ttlMs?: number;\n /** Max cache entries (default: 1000) */\n maxSize?: number;\n}\n\nexport interface MastraRBACNeonOptions {\n /**\n * Base URL for the Neon Auth service.\n * Falls back to `NEON_AUTH_BASE_URL` env var.\n */\n baseUrl?: string;\n /**\n * Map provider role slugs to arrays of Mastra permission patterns.\n * Use `_default` for roles not explicitly listed.\n *\n * @example\n * ```typescript\n * {\n * owner: ['*'],\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * _default: [],\n * }\n * ```\n */\n roleMapping: RoleMapping;\n /**\n * Specific organization ID to scope role lookups to.\n * If omitted, roles from all organizations are merged.\n */\n organizationId?: string;\n /** Caching options for role lookups. */\n cache?: NeonRoleMappingOptions;\n /**\n * Custom function to extract roles from the NeonAuthUser object.\n * Useful when JWT claims already contain role info.\n */\n getUserRoles?: (user: NeonAuthUser) => string[] | Promise<string[]>;\n}\n\nconst DEFAULT_CACHE_TTL_MS = 60_000;\nconst DEFAULT_CACHE_MAX_SIZE = 1000;\n\ninterface CacheEntry {\n roles: string[];\n expiresAt: number;\n}\n\n/**\n * RBAC provider for Neon Auth that maps organization roles to Mastra permissions.\n *\n * Neon Auth uses Better Auth's Organization plugin which provides `owner`,\n * `admin`, and `member` roles. This provider maps those roles to Mastra\n * permission patterns via a configurable `roleMapping`.\n *\n * @example Static mapping\n * ```typescript\n * import { MastraRBACNeon } from '@mastra/auth-neon';\n *\n * const rbac = new MastraRBACNeon({\n * roleMapping: {\n * owner: ['*'],\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * _default: [],\n * },\n * });\n * ```\n *\n * @example With custom role extraction from JWT claims\n * ```typescript\n * const rbac = new MastraRBACNeon({\n * roleMapping: {\n * admin: ['*'],\n * member: ['agents:read'],\n * },\n * getUserRoles: (user) => {\n * const role = user.jwt?.role as string;\n * return role ? [role] : [];\n * },\n * });\n * ```\n */\nexport class MastraRBACNeon implements IRBACProvider<NeonAuthUser> {\n private options: MastraRBACNeonOptions;\n private baseUrl: string;\n private rolesCache: Map<string, CacheEntry> = new Map();\n private cacheTtlMs: number;\n private cacheMaxSize: number;\n\n get roleMapping(): RoleMapping {\n return this.options.roleMapping;\n }\n\n constructor(options: MastraRBACNeonOptions) {\n this.options = options;\n const rawUrl = options.baseUrl ?? process.env.NEON_AUTH_BASE_URL ?? '';\n let end = rawUrl.length;\n while (end > 0 && rawUrl[end - 1] === '/') {\n end--;\n }\n this.baseUrl = rawUrl.slice(0, end);\n this.cacheTtlMs = options.cache?.ttlMs ?? DEFAULT_CACHE_TTL_MS;\n this.cacheMaxSize = options.cache?.maxSize ?? DEFAULT_CACHE_MAX_SIZE;\n }\n\n async getRoles(user: NeonAuthUser): Promise<string[]> {\n if (this.options.getUserRoles) {\n return this.options.getUserRoles(user);\n }\n\n // Try extracting role from JWT claims first (fast path).\n if (user.jwt) {\n const role = user.jwt.role as string | undefined;\n if (role) return [role];\n }\n\n const userId = user.user?.id;\n if (!userId) return [];\n\n // Check cache (promote on hit for LRU eviction).\n const cached = this.rolesCache.get(userId);\n if (cached && cached.expiresAt > Date.now()) {\n this.rolesCache.delete(userId);\n this.rolesCache.set(userId, cached);\n return cached.roles;\n }\n\n // Fetch organization memberships from Neon Auth.\n const roles = await this.fetchRolesFromNeonAuth(userId);\n\n // Cache the result.\n if (this.rolesCache.size >= this.cacheMaxSize) {\n const firstKey = this.rolesCache.keys().next().value;\n if (firstKey) this.rolesCache.delete(firstKey);\n }\n this.rolesCache.set(userId, { roles, expiresAt: Date.now() + this.cacheTtlMs });\n\n return roles;\n }\n\n async hasRole(user: NeonAuthUser, role: string): Promise<boolean> {\n const roles = await this.getRoles(user);\n return roles.includes(role);\n }\n\n async getPermissions(user: NeonAuthUser): Promise<string[]> {\n const roles = await this.getRoles(user);\n return resolvePermissionsFromMapping(roles, this.options.roleMapping);\n }\n\n async hasPermission(user: NeonAuthUser, 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: NeonAuthUser, 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: NeonAuthUser, 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 async getAvailableRoles(): Promise<{ id: string; name: string }[]> {\n return Object.keys(this.options.roleMapping)\n .filter(k => k !== '_default')\n .map(k => ({ id: k, name: k.charAt(0).toUpperCase() + k.slice(1) }));\n }\n\n async getRolePermissions(roleId: string): Promise<string[]> {\n return resolvePermissionsFromMapping([roleId], this.options.roleMapping);\n }\n\n /**\n * Fetch organization membership roles from Neon Auth.\n *\n * Uses Better Auth's `organization/list-memberships` admin API.\n * Falls back to empty roles on error (default permissions will apply).\n */\n private async fetchRolesFromNeonAuth(userId: string): Promise<string[]> {\n if (!this.baseUrl) return [];\n\n try {\n const response = await fetch(`${this.baseUrl}/auth/api/organization/list-memberships`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ userId }),\n });\n\n if (!response.ok) return [];\n\n const data = (await response.json()) as NeonOrgMember[] | { data?: NeonOrgMember[] };\n const memberships = Array.isArray(data) ? data : (data.data ?? []);\n\n const relevant = this.options.organizationId\n ? memberships.filter(m => m.organizationId === this.options.organizationId)\n : memberships;\n\n return relevant.map(m => m.role);\n } catch {\n return [];\n }\n }\n}\n","import type {\n IUserProvider,\n ICredentialsProvider,\n ISessionProvider,\n Session,\n CredentialsResult,\n} from '@mastra/core/auth';\nimport type { EEUser } from '@mastra/core/auth/ee';\nimport type { MastraAuthProviderOptions } from '@mastra/core/server';\nimport { MastraAuthProvider } from '@mastra/core/server';\n\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\nimport type { JWTPayload } from 'jose';\n\nexport { MastraRBACNeon } from './rbac-provider';\nexport type { MastraRBACNeonOptions, NeonRoleMappingOptions } from './rbac-provider';\n\ntype HonoRequestLike = {\n raw?: Request;\n headers?: Headers;\n header(name: string): string | undefined;\n};\n\ntype MastraAuthRequest = Request | HonoRequestLike;\n\nfunction getRequestHeader(request: MastraAuthRequest, name: string): string | null {\n if (request instanceof Request) {\n return request.headers.get(name);\n }\n return request.raw?.headers.get(name) ?? request.headers?.get(name) ?? request.header(name) ?? null;\n}\n\nfunction stripTrailingSlashes(url: string): string {\n let end = url.length;\n while (end > 0 && url[end - 1] === '/') {\n end--;\n }\n return url.slice(0, end);\n}\n\nfunction parseCookies(response: Response): string[] {\n if (typeof response.headers.getSetCookie === 'function') {\n return response.headers.getSetCookie();\n }\n const raw = response.headers.get('set-cookie');\n if (!raw) return [];\n return raw.split(/,(?=\\s*\\w+=)/);\n}\n\n/**\n * Response shape from Neon Auth session endpoint.\n */\nexport interface NeonSessionResponse {\n session: {\n id: string;\n token: string;\n userId: string;\n expiresAt: string;\n createdAt: string;\n updatedAt: string;\n };\n user: {\n id: string;\n email: string;\n name: string;\n image?: string | null;\n emailVerified: boolean;\n createdAt: string;\n updatedAt: string;\n };\n}\n\n/**\n * User type returned by the adapter's authenticateToken.\n * Contains the session and user data from Neon Auth,\n * plus optional JWT claims when authenticated via bearer JWT.\n */\nexport interface NeonAuthUser {\n session?: NeonSessionResponse['session'];\n user: NeonSessionResponse['user'];\n jwt?: JWTPayload;\n}\n\nexport function mapNeonUserToEEUser(user: NeonSessionResponse['user']): EEUser {\n return {\n id: user.id,\n email: user.email,\n name: user.name,\n avatarUrl: user.image ?? undefined,\n metadata: {\n emailVerified: user.emailVerified,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt,\n },\n };\n}\n\nexport interface MastraAuthNeonOptions extends MastraAuthProviderOptions<NeonAuthUser> {\n /**\n * The Neon Auth base URL (e.g., `https://your-project.neon.tech`).\n * Falls back to the `NEON_AUTH_BASE_URL` environment variable.\n */\n baseUrl?: string;\n /**\n * Explicit JWKS URL for JWT token verification.\n * Overrides the URL derived from `baseUrl`.\n * Falls back to the `NEON_AUTH_JWKS_URL` environment variable.\n */\n jwksUrl?: string;\n /**\n * The session cookie name used by Neon Auth.\n * @default 'neonauth.session_token'\n */\n sessionCookieName?: string;\n /**\n * Whether to allow new user registration via sign-up.\n * @default true\n */\n signUpEnabled?: boolean;\n}\n\n/**\n * Mastra authentication provider for Neon Auth.\n *\n * Neon Auth is a managed authentication service built on Better Auth\n * that stores users, sessions, and auth configuration directly in your\n * Neon Postgres database.\n *\n * This adapter supports:\n * - JWT bearer token verification via JWKS (for API clients)\n * - Session cookie verification via Neon Auth REST API (for Studio)\n * - Email/password sign-in and sign-up (for Studio credentials flow)\n * - Session management (validate, refresh, destroy)\n *\n * @example\n * ```typescript\n * import { MastraAuthNeon } from '@mastra/auth-neon';\n *\n * const auth = new MastraAuthNeon({\n * baseUrl: process.env.NEON_AUTH_BASE_URL,\n * });\n *\n * const mastra = new Mastra({\n * server: {\n * auth,\n * },\n * });\n * ```\n *\n * @example With RBAC\n * ```typescript\n * import { MastraAuthNeon, MastraRBACNeon } from '@mastra/auth-neon';\n *\n * const mastra = new Mastra({\n * server: {\n * auth: new MastraAuthNeon({\n * baseUrl: process.env.NEON_AUTH_BASE_URL,\n * }),\n * rbac: new MastraRBACNeon({\n * roleMapping: {\n * owner: ['*'],\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * _default: [],\n * },\n * }),\n * },\n * });\n * ```\n *\n * @see https://neon.com/docs/auth/overview\n */\nexport class MastraAuthNeon\n extends MastraAuthProvider<NeonAuthUser>\n implements IUserProvider<EEUser>, ICredentialsProvider<EEUser>, ISessionProvider<Session>\n{\n protected baseUrl: string;\n protected jwksUrl: string;\n public sessionCookieName: string;\n protected signUpEnabledConfig: boolean;\n\n constructor(options?: MastraAuthNeonOptions) {\n super({ name: options?.name ?? 'neon' });\n\n const baseUrl = options?.baseUrl ?? process.env.NEON_AUTH_BASE_URL;\n\n if (!baseUrl) {\n throw new Error(\n 'Neon Auth base URL is required, please provide it in the options or set the NEON_AUTH_BASE_URL environment variable',\n );\n }\n\n this.baseUrl = stripTrailingSlashes(baseUrl);\n this.jwksUrl = options?.jwksUrl ?? process.env.NEON_AUTH_JWKS_URL ?? `${this.baseUrl}/auth/jwks`;\n this.sessionCookieName = options?.sessionCookieName ?? 'neonauth.session_token';\n this.signUpEnabledConfig = options?.signUpEnabled ?? true;\n\n this.registerOptions(options);\n }\n\n /** Expose the base URL for RBAC or other consumers. */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n isSignUpEnabled(): boolean {\n return this.signUpEnabledConfig;\n }\n\n // ── IUserProvider ──\n\n async getCurrentUser(request: Request): Promise<EEUser | null> {\n try {\n const result = await this.fetchSession(request.headers);\n if (!result?.user) return null;\n return mapNeonUserToEEUser(result.user);\n } catch {\n return null;\n }\n }\n\n async getUser(_userId: string): Promise<EEUser | null> {\n return null;\n }\n\n getUserProfileUrl(user: EEUser): string {\n return `/profile/${user.id}`;\n }\n\n // ── MastraAuthProvider ──\n\n async authenticateToken(token: string, request: MastraAuthRequest): Promise<NeonAuthUser | null> {\n if (!token || typeof token !== 'string') {\n return null;\n }\n\n // Try JWT verification first (for bearer JWT tokens from API clients).\n const jwtResult = await this.verifyJwt(token);\n if (jwtResult) {\n return {\n user: {\n id: jwtResult.sub ?? '',\n email: (jwtResult.email as string) ?? '',\n name: (jwtResult.name as string) ?? '',\n image: (jwtResult.picture as string) ?? null,\n emailVerified: (jwtResult.email_verified as boolean) ?? false,\n createdAt: jwtResult.iat ? new Date(jwtResult.iat * 1000).toISOString() : '',\n updatedAt: '',\n },\n jwt: jwtResult,\n };\n }\n\n // Fall back to session cookie verification via Neon Auth API.\n try {\n const headers = new Headers();\n\n const cookieHeader = getRequestHeader(request, 'Cookie');\n if (cookieHeader) {\n headers.set('Cookie', cookieHeader);\n }\n\n const hasSessionCookie = !!cookieHeader\n ?.split(';')\n .some(pair => pair.trim().split('=')[0]?.trim() === this.sessionCookieName);\n\n if (token && !hasSessionCookie) {\n const existingCookies = cookieHeader ? `${cookieHeader}; ` : '';\n headers.set('Cookie', `${existingCookies}${this.sessionCookieName}=${token}`);\n }\n\n const result = await this.fetchSession(headers);\n if (!result?.session || !result?.user) {\n return null;\n }\n\n return {\n session: result.session,\n user: result.user,\n };\n } catch {\n return null;\n }\n }\n\n async authorizeUser(user: NeonAuthUser): Promise<boolean> {\n if (!user?.user?.id) return false;\n\n if (user.jwt?.exp && user.jwt.exp * 1000 < Date.now()) {\n return false;\n }\n\n return true;\n }\n\n // ── ICredentialsProvider ──\n\n async signIn(email: string, password: string, request: Request): Promise<CredentialsResult<EEUser>> {\n const response = await fetch(`${this.baseUrl}/auth/sign-in/email`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(request?.headers ? Object.fromEntries(request.headers.entries()) : {}),\n },\n body: JSON.stringify({ email, password }),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as { message?: string };\n throw new Error(errorData.message || 'Invalid email or password');\n }\n\n const result = (await response.json()) as { user?: NeonSessionResponse['user']; token?: string | null };\n\n if (!result?.user) {\n throw new Error('Invalid email or password');\n }\n\n const cookies = parseCookies(response);\n\n return {\n user: mapNeonUserToEEUser(result.user),\n token: result.token ?? undefined,\n cookies,\n };\n }\n\n async signUp(\n email: string,\n password: string,\n name: string | undefined,\n request: Request,\n ): Promise<CredentialsResult<EEUser>> {\n const displayName = name ?? email.split('@')[0] ?? 'User';\n\n const response = await fetch(`${this.baseUrl}/auth/sign-up/email`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(request?.headers ? Object.fromEntries(request.headers.entries()) : {}),\n },\n body: JSON.stringify({ email, password, name: displayName }),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as { message?: string };\n throw new Error(errorData.message || 'Failed to create account');\n }\n\n const result = (await response.json()) as { user?: NeonSessionResponse['user']; token?: string | null };\n\n if (!result?.user) {\n throw new Error('Failed to create account');\n }\n\n const cookies = parseCookies(response);\n\n return {\n user: mapNeonUserToEEUser(result.user),\n token: result.token ?? undefined,\n cookies,\n };\n }\n\n // ── ISessionProvider ──\n\n async createSession(_userId: string, metadata?: Record<string, unknown>): Promise<Session> {\n const now = new Date();\n const expiresAt = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);\n return {\n id: crypto.randomUUID(),\n userId: _userId,\n createdAt: now,\n expiresAt,\n metadata,\n };\n }\n\n async validateSession(sessionId: string): Promise<Session | null> {\n try {\n const headers = new Headers();\n headers.set('Cookie', `${this.sessionCookieName}=${sessionId}`);\n const result = await this.fetchSession(headers);\n if (!result?.session) return null;\n\n return {\n id: result.session.id,\n userId: result.session.userId,\n expiresAt: new Date(result.session.expiresAt),\n createdAt: new Date(result.session.createdAt),\n };\n } catch {\n return null;\n }\n }\n\n async destroySession(_sessionId: string): Promise<void> {\n // Neon Auth (Better Auth) manages session destruction via the sign-out endpoint.\n // Cookie clearing happens via getClearSessionHeaders.\n }\n\n async refreshSession(sessionId: string): Promise<Session | null> {\n // Neon Auth (Better Auth) refreshes sessions automatically when\n // get-session is called and the updateAge threshold is reached.\n return this.validateSession(sessionId);\n }\n\n getSessionIdFromRequest(request: Request): string | null {\n const cookieHeader = request.headers.get('Cookie');\n if (!cookieHeader) return null;\n\n for (const pair of cookieHeader.split(';')) {\n const [key, ...rest] = pair.trim().split('=');\n if (key?.trim() === this.sessionCookieName) {\n return rest.join('=') || null;\n }\n }\n\n return null;\n }\n\n getSessionHeaders(session: Session): Record<string, string> {\n const cookie = (session as unknown as Record<string, unknown>)._sessionCookie;\n if (typeof cookie === 'string') {\n return { 'Set-Cookie': cookie };\n }\n return {};\n }\n\n getClearSessionHeaders(): Record<string, string> {\n const cookies = [\n `${this.sessionCookieName}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`,\n `${this.sessionCookieName}_sig=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`,\n ];\n return {\n 'Set-Cookie': cookies.join(', '),\n };\n }\n\n // ── Internal helpers ──\n\n private async verifyJwt(token: string): Promise<JWTPayload | null> {\n try {\n const JWKS = createRemoteJWKSet(new URL(this.jwksUrl));\n const { payload } = await jwtVerify(token, JWKS);\n return payload;\n } catch {\n return null;\n }\n }\n\n /** Fetch and validate a session from the Neon Auth REST API. */\n public async fetchSession(headers: Headers): Promise<NeonSessionResponse | null> {\n const response = await fetch(`${this.baseUrl}/auth/get-session`, {\n method: 'GET',\n headers,\n });\n\n if (!response.ok) {\n return null;\n }\n\n const data = (await response.json()) as NeonSessionResponse | null;\n if (!data?.session || !data?.user) {\n return null;\n }\n\n return data;\n }\n}\n"]}
@@ -0,0 +1,148 @@
1
+ import type { IUserProvider, ICredentialsProvider, ISessionProvider, Session, CredentialsResult } from '@mastra/core/auth';
2
+ import type { EEUser } from '@mastra/core/auth/ee';
3
+ import type { MastraAuthProviderOptions } from '@mastra/core/server';
4
+ import { MastraAuthProvider } from '@mastra/core/server';
5
+ import type { JWTPayload } from 'jose';
6
+ export { MastraRBACNeon } from './rbac-provider.js';
7
+ export type { MastraRBACNeonOptions, NeonRoleMappingOptions } from './rbac-provider.js';
8
+ type HonoRequestLike = {
9
+ raw?: Request;
10
+ headers?: Headers;
11
+ header(name: string): string | undefined;
12
+ };
13
+ type MastraAuthRequest = Request | HonoRequestLike;
14
+ /**
15
+ * Response shape from Neon Auth session endpoint.
16
+ */
17
+ export interface NeonSessionResponse {
18
+ session: {
19
+ id: string;
20
+ token: string;
21
+ userId: string;
22
+ expiresAt: string;
23
+ createdAt: string;
24
+ updatedAt: string;
25
+ };
26
+ user: {
27
+ id: string;
28
+ email: string;
29
+ name: string;
30
+ image?: string | null;
31
+ emailVerified: boolean;
32
+ createdAt: string;
33
+ updatedAt: string;
34
+ };
35
+ }
36
+ /**
37
+ * User type returned by the adapter's authenticateToken.
38
+ * Contains the session and user data from Neon Auth,
39
+ * plus optional JWT claims when authenticated via bearer JWT.
40
+ */
41
+ export interface NeonAuthUser {
42
+ session?: NeonSessionResponse['session'];
43
+ user: NeonSessionResponse['user'];
44
+ jwt?: JWTPayload;
45
+ }
46
+ export declare function mapNeonUserToEEUser(user: NeonSessionResponse['user']): EEUser;
47
+ export interface MastraAuthNeonOptions extends MastraAuthProviderOptions<NeonAuthUser> {
48
+ /**
49
+ * The Neon Auth base URL (e.g., `https://your-project.neon.tech`).
50
+ * Falls back to the `NEON_AUTH_BASE_URL` environment variable.
51
+ */
52
+ baseUrl?: string;
53
+ /**
54
+ * Explicit JWKS URL for JWT token verification.
55
+ * Overrides the URL derived from `baseUrl`.
56
+ * Falls back to the `NEON_AUTH_JWKS_URL` environment variable.
57
+ */
58
+ jwksUrl?: string;
59
+ /**
60
+ * The session cookie name used by Neon Auth.
61
+ * @default 'neonauth.session_token'
62
+ */
63
+ sessionCookieName?: string;
64
+ /**
65
+ * Whether to allow new user registration via sign-up.
66
+ * @default true
67
+ */
68
+ signUpEnabled?: boolean;
69
+ }
70
+ /**
71
+ * Mastra authentication provider for Neon Auth.
72
+ *
73
+ * Neon Auth is a managed authentication service built on Better Auth
74
+ * that stores users, sessions, and auth configuration directly in your
75
+ * Neon Postgres database.
76
+ *
77
+ * This adapter supports:
78
+ * - JWT bearer token verification via JWKS (for API clients)
79
+ * - Session cookie verification via Neon Auth REST API (for Studio)
80
+ * - Email/password sign-in and sign-up (for Studio credentials flow)
81
+ * - Session management (validate, refresh, destroy)
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * import { MastraAuthNeon } from '@mastra/auth-neon';
86
+ *
87
+ * const auth = new MastraAuthNeon({
88
+ * baseUrl: process.env.NEON_AUTH_BASE_URL,
89
+ * });
90
+ *
91
+ * const mastra = new Mastra({
92
+ * server: {
93
+ * auth,
94
+ * },
95
+ * });
96
+ * ```
97
+ *
98
+ * @example With RBAC
99
+ * ```typescript
100
+ * import { MastraAuthNeon, MastraRBACNeon } from '@mastra/auth-neon';
101
+ *
102
+ * const mastra = new Mastra({
103
+ * server: {
104
+ * auth: new MastraAuthNeon({
105
+ * baseUrl: process.env.NEON_AUTH_BASE_URL,
106
+ * }),
107
+ * rbac: new MastraRBACNeon({
108
+ * roleMapping: {
109
+ * owner: ['*'],
110
+ * admin: ['*'],
111
+ * member: ['agents:read', 'workflows:*'],
112
+ * _default: [],
113
+ * },
114
+ * }),
115
+ * },
116
+ * });
117
+ * ```
118
+ *
119
+ * @see https://neon.com/docs/auth/overview
120
+ */
121
+ export declare class MastraAuthNeon extends MastraAuthProvider<NeonAuthUser> implements IUserProvider<EEUser>, ICredentialsProvider<EEUser>, ISessionProvider<Session> {
122
+ protected baseUrl: string;
123
+ protected jwksUrl: string;
124
+ sessionCookieName: string;
125
+ protected signUpEnabledConfig: boolean;
126
+ constructor(options?: MastraAuthNeonOptions);
127
+ /** Expose the base URL for RBAC or other consumers. */
128
+ getBaseUrl(): string;
129
+ isSignUpEnabled(): boolean;
130
+ getCurrentUser(request: Request): Promise<EEUser | null>;
131
+ getUser(_userId: string): Promise<EEUser | null>;
132
+ getUserProfileUrl(user: EEUser): string;
133
+ authenticateToken(token: string, request: MastraAuthRequest): Promise<NeonAuthUser | null>;
134
+ authorizeUser(user: NeonAuthUser): Promise<boolean>;
135
+ signIn(email: string, password: string, request: Request): Promise<CredentialsResult<EEUser>>;
136
+ signUp(email: string, password: string, name: string | undefined, request: Request): Promise<CredentialsResult<EEUser>>;
137
+ createSession(_userId: string, metadata?: Record<string, unknown>): Promise<Session>;
138
+ validateSession(sessionId: string): Promise<Session | null>;
139
+ destroySession(_sessionId: string): Promise<void>;
140
+ refreshSession(sessionId: string): Promise<Session | null>;
141
+ getSessionIdFromRequest(request: Request): string | null;
142
+ getSessionHeaders(session: Session): Record<string, string>;
143
+ getClearSessionHeaders(): Record<string, string>;
144
+ private verifyJwt;
145
+ /** Fetch and validate a session from the Neon Auth REST API. */
146
+ fetchSession(headers: Headers): Promise<NeonSessionResponse | null>;
147
+ }
148
+ //# 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,369 @@
1
+ import { MastraAuthProvider } from '@mastra/core/server';
2
+ import { createRemoteJWKSet, jwtVerify } from 'jose';
3
+ import { resolvePermissionsFromMapping, matchesPermission } from '@mastra/core/auth/ee';
4
+
5
+ // src/index.ts
6
+ var DEFAULT_CACHE_TTL_MS = 6e4;
7
+ var DEFAULT_CACHE_MAX_SIZE = 1e3;
8
+ var MastraRBACNeon = class {
9
+ options;
10
+ baseUrl;
11
+ rolesCache = /* @__PURE__ */ new Map();
12
+ cacheTtlMs;
13
+ cacheMaxSize;
14
+ get roleMapping() {
15
+ return this.options.roleMapping;
16
+ }
17
+ constructor(options) {
18
+ this.options = options;
19
+ const rawUrl = options.baseUrl ?? process.env.NEON_AUTH_BASE_URL ?? "";
20
+ let end = rawUrl.length;
21
+ while (end > 0 && rawUrl[end - 1] === "/") {
22
+ end--;
23
+ }
24
+ this.baseUrl = rawUrl.slice(0, end);
25
+ this.cacheTtlMs = options.cache?.ttlMs ?? DEFAULT_CACHE_TTL_MS;
26
+ this.cacheMaxSize = options.cache?.maxSize ?? DEFAULT_CACHE_MAX_SIZE;
27
+ }
28
+ async getRoles(user) {
29
+ if (this.options.getUserRoles) {
30
+ return this.options.getUserRoles(user);
31
+ }
32
+ if (user.jwt) {
33
+ const role = user.jwt.role;
34
+ if (role) return [role];
35
+ }
36
+ const userId = user.user?.id;
37
+ if (!userId) return [];
38
+ const cached = this.rolesCache.get(userId);
39
+ if (cached && cached.expiresAt > Date.now()) {
40
+ this.rolesCache.delete(userId);
41
+ this.rolesCache.set(userId, cached);
42
+ return cached.roles;
43
+ }
44
+ const roles = await this.fetchRolesFromNeonAuth(userId);
45
+ if (this.rolesCache.size >= this.cacheMaxSize) {
46
+ const firstKey = this.rolesCache.keys().next().value;
47
+ if (firstKey) this.rolesCache.delete(firstKey);
48
+ }
49
+ this.rolesCache.set(userId, { roles, expiresAt: Date.now() + this.cacheTtlMs });
50
+ return roles;
51
+ }
52
+ async hasRole(user, role) {
53
+ const roles = await this.getRoles(user);
54
+ return roles.includes(role);
55
+ }
56
+ async getPermissions(user) {
57
+ const roles = await this.getRoles(user);
58
+ return resolvePermissionsFromMapping(roles, this.options.roleMapping);
59
+ }
60
+ async hasPermission(user, permission) {
61
+ const permissions = await this.getPermissions(user);
62
+ return permissions.some((p) => matchesPermission(p, permission));
63
+ }
64
+ async hasAllPermissions(user, permissions) {
65
+ const userPermissions = await this.getPermissions(user);
66
+ return permissions.every((required) => userPermissions.some((p) => matchesPermission(p, required)));
67
+ }
68
+ async hasAnyPermission(user, permissions) {
69
+ const userPermissions = await this.getPermissions(user);
70
+ return permissions.some((required) => userPermissions.some((p) => matchesPermission(p, required)));
71
+ }
72
+ async getAvailableRoles() {
73
+ return Object.keys(this.options.roleMapping).filter((k) => k !== "_default").map((k) => ({ id: k, name: k.charAt(0).toUpperCase() + k.slice(1) }));
74
+ }
75
+ async getRolePermissions(roleId) {
76
+ return resolvePermissionsFromMapping([roleId], this.options.roleMapping);
77
+ }
78
+ /**
79
+ * Fetch organization membership roles from Neon Auth.
80
+ *
81
+ * Uses Better Auth's `organization/list-memberships` admin API.
82
+ * Falls back to empty roles on error (default permissions will apply).
83
+ */
84
+ async fetchRolesFromNeonAuth(userId) {
85
+ if (!this.baseUrl) return [];
86
+ try {
87
+ const response = await fetch(`${this.baseUrl}/auth/api/organization/list-memberships`, {
88
+ method: "POST",
89
+ headers: { "Content-Type": "application/json" },
90
+ body: JSON.stringify({ userId })
91
+ });
92
+ if (!response.ok) return [];
93
+ const data = await response.json();
94
+ const memberships = Array.isArray(data) ? data : data.data ?? [];
95
+ const relevant = this.options.organizationId ? memberships.filter((m) => m.organizationId === this.options.organizationId) : memberships;
96
+ return relevant.map((m) => m.role);
97
+ } catch {
98
+ return [];
99
+ }
100
+ }
101
+ };
102
+
103
+ // src/index.ts
104
+ function getRequestHeader(request, name) {
105
+ if (request instanceof Request) {
106
+ return request.headers.get(name);
107
+ }
108
+ return request.raw?.headers.get(name) ?? request.headers?.get(name) ?? request.header(name) ?? null;
109
+ }
110
+ function stripTrailingSlashes(url) {
111
+ let end = url.length;
112
+ while (end > 0 && url[end - 1] === "/") {
113
+ end--;
114
+ }
115
+ return url.slice(0, end);
116
+ }
117
+ function parseCookies(response) {
118
+ if (typeof response.headers.getSetCookie === "function") {
119
+ return response.headers.getSetCookie();
120
+ }
121
+ const raw = response.headers.get("set-cookie");
122
+ if (!raw) return [];
123
+ return raw.split(/,(?=\s*\w+=)/);
124
+ }
125
+ function mapNeonUserToEEUser(user) {
126
+ return {
127
+ id: user.id,
128
+ email: user.email,
129
+ name: user.name,
130
+ avatarUrl: user.image ?? void 0,
131
+ metadata: {
132
+ emailVerified: user.emailVerified,
133
+ createdAt: user.createdAt,
134
+ updatedAt: user.updatedAt
135
+ }
136
+ };
137
+ }
138
+ var MastraAuthNeon = class extends MastraAuthProvider {
139
+ baseUrl;
140
+ jwksUrl;
141
+ sessionCookieName;
142
+ signUpEnabledConfig;
143
+ constructor(options) {
144
+ super({ name: options?.name ?? "neon" });
145
+ const baseUrl = options?.baseUrl ?? process.env.NEON_AUTH_BASE_URL;
146
+ if (!baseUrl) {
147
+ throw new Error(
148
+ "Neon Auth base URL is required, please provide it in the options or set the NEON_AUTH_BASE_URL environment variable"
149
+ );
150
+ }
151
+ this.baseUrl = stripTrailingSlashes(baseUrl);
152
+ this.jwksUrl = options?.jwksUrl ?? process.env.NEON_AUTH_JWKS_URL ?? `${this.baseUrl}/auth/jwks`;
153
+ this.sessionCookieName = options?.sessionCookieName ?? "neonauth.session_token";
154
+ this.signUpEnabledConfig = options?.signUpEnabled ?? true;
155
+ this.registerOptions(options);
156
+ }
157
+ /** Expose the base URL for RBAC or other consumers. */
158
+ getBaseUrl() {
159
+ return this.baseUrl;
160
+ }
161
+ isSignUpEnabled() {
162
+ return this.signUpEnabledConfig;
163
+ }
164
+ // ── IUserProvider ──
165
+ async getCurrentUser(request) {
166
+ try {
167
+ const result = await this.fetchSession(request.headers);
168
+ if (!result?.user) return null;
169
+ return mapNeonUserToEEUser(result.user);
170
+ } catch {
171
+ return null;
172
+ }
173
+ }
174
+ async getUser(_userId) {
175
+ return null;
176
+ }
177
+ getUserProfileUrl(user) {
178
+ return `/profile/${user.id}`;
179
+ }
180
+ // ── MastraAuthProvider ──
181
+ async authenticateToken(token, request) {
182
+ if (!token || typeof token !== "string") {
183
+ return null;
184
+ }
185
+ const jwtResult = await this.verifyJwt(token);
186
+ if (jwtResult) {
187
+ return {
188
+ user: {
189
+ id: jwtResult.sub ?? "",
190
+ email: jwtResult.email ?? "",
191
+ name: jwtResult.name ?? "",
192
+ image: jwtResult.picture ?? null,
193
+ emailVerified: jwtResult.email_verified ?? false,
194
+ createdAt: jwtResult.iat ? new Date(jwtResult.iat * 1e3).toISOString() : "",
195
+ updatedAt: ""
196
+ },
197
+ jwt: jwtResult
198
+ };
199
+ }
200
+ try {
201
+ const headers = new Headers();
202
+ const cookieHeader = getRequestHeader(request, "Cookie");
203
+ if (cookieHeader) {
204
+ headers.set("Cookie", cookieHeader);
205
+ }
206
+ const hasSessionCookie = !!cookieHeader?.split(";").some((pair) => pair.trim().split("=")[0]?.trim() === this.sessionCookieName);
207
+ if (token && !hasSessionCookie) {
208
+ const existingCookies = cookieHeader ? `${cookieHeader}; ` : "";
209
+ headers.set("Cookie", `${existingCookies}${this.sessionCookieName}=${token}`);
210
+ }
211
+ const result = await this.fetchSession(headers);
212
+ if (!result?.session || !result?.user) {
213
+ return null;
214
+ }
215
+ return {
216
+ session: result.session,
217
+ user: result.user
218
+ };
219
+ } catch {
220
+ return null;
221
+ }
222
+ }
223
+ async authorizeUser(user) {
224
+ if (!user?.user?.id) return false;
225
+ if (user.jwt?.exp && user.jwt.exp * 1e3 < Date.now()) {
226
+ return false;
227
+ }
228
+ return true;
229
+ }
230
+ // ── ICredentialsProvider ──
231
+ async signIn(email, password, request) {
232
+ const response = await fetch(`${this.baseUrl}/auth/sign-in/email`, {
233
+ method: "POST",
234
+ headers: {
235
+ "Content-Type": "application/json",
236
+ ...request?.headers ? Object.fromEntries(request.headers.entries()) : {}
237
+ },
238
+ body: JSON.stringify({ email, password })
239
+ });
240
+ if (!response.ok) {
241
+ const errorData = await response.json().catch(() => ({}));
242
+ throw new Error(errorData.message || "Invalid email or password");
243
+ }
244
+ const result = await response.json();
245
+ if (!result?.user) {
246
+ throw new Error("Invalid email or password");
247
+ }
248
+ const cookies = parseCookies(response);
249
+ return {
250
+ user: mapNeonUserToEEUser(result.user),
251
+ token: result.token ?? void 0,
252
+ cookies
253
+ };
254
+ }
255
+ async signUp(email, password, name, request) {
256
+ const displayName = name ?? email.split("@")[0] ?? "User";
257
+ const response = await fetch(`${this.baseUrl}/auth/sign-up/email`, {
258
+ method: "POST",
259
+ headers: {
260
+ "Content-Type": "application/json",
261
+ ...request?.headers ? Object.fromEntries(request.headers.entries()) : {}
262
+ },
263
+ body: JSON.stringify({ email, password, name: displayName })
264
+ });
265
+ if (!response.ok) {
266
+ const errorData = await response.json().catch(() => ({}));
267
+ throw new Error(errorData.message || "Failed to create account");
268
+ }
269
+ const result = await response.json();
270
+ if (!result?.user) {
271
+ throw new Error("Failed to create account");
272
+ }
273
+ const cookies = parseCookies(response);
274
+ return {
275
+ user: mapNeonUserToEEUser(result.user),
276
+ token: result.token ?? void 0,
277
+ cookies
278
+ };
279
+ }
280
+ // ── ISessionProvider ──
281
+ async createSession(_userId, metadata) {
282
+ const now = /* @__PURE__ */ new Date();
283
+ const expiresAt = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1e3);
284
+ return {
285
+ id: crypto.randomUUID(),
286
+ userId: _userId,
287
+ createdAt: now,
288
+ expiresAt,
289
+ metadata
290
+ };
291
+ }
292
+ async validateSession(sessionId) {
293
+ try {
294
+ const headers = new Headers();
295
+ headers.set("Cookie", `${this.sessionCookieName}=${sessionId}`);
296
+ const result = await this.fetchSession(headers);
297
+ if (!result?.session) return null;
298
+ return {
299
+ id: result.session.id,
300
+ userId: result.session.userId,
301
+ expiresAt: new Date(result.session.expiresAt),
302
+ createdAt: new Date(result.session.createdAt)
303
+ };
304
+ } catch {
305
+ return null;
306
+ }
307
+ }
308
+ async destroySession(_sessionId) {
309
+ }
310
+ async refreshSession(sessionId) {
311
+ return this.validateSession(sessionId);
312
+ }
313
+ getSessionIdFromRequest(request) {
314
+ const cookieHeader = request.headers.get("Cookie");
315
+ if (!cookieHeader) return null;
316
+ for (const pair of cookieHeader.split(";")) {
317
+ const [key, ...rest] = pair.trim().split("=");
318
+ if (key?.trim() === this.sessionCookieName) {
319
+ return rest.join("=") || null;
320
+ }
321
+ }
322
+ return null;
323
+ }
324
+ getSessionHeaders(session) {
325
+ const cookie = session._sessionCookie;
326
+ if (typeof cookie === "string") {
327
+ return { "Set-Cookie": cookie };
328
+ }
329
+ return {};
330
+ }
331
+ getClearSessionHeaders() {
332
+ const cookies = [
333
+ `${this.sessionCookieName}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`,
334
+ `${this.sessionCookieName}_sig=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`
335
+ ];
336
+ return {
337
+ "Set-Cookie": cookies.join(", ")
338
+ };
339
+ }
340
+ // ── Internal helpers ──
341
+ async verifyJwt(token) {
342
+ try {
343
+ const JWKS = createRemoteJWKSet(new URL(this.jwksUrl));
344
+ const { payload } = await jwtVerify(token, JWKS);
345
+ return payload;
346
+ } catch {
347
+ return null;
348
+ }
349
+ }
350
+ /** Fetch and validate a session from the Neon Auth REST API. */
351
+ async fetchSession(headers) {
352
+ const response = await fetch(`${this.baseUrl}/auth/get-session`, {
353
+ method: "GET",
354
+ headers
355
+ });
356
+ if (!response.ok) {
357
+ return null;
358
+ }
359
+ const data = await response.json();
360
+ if (!data?.session || !data?.user) {
361
+ return null;
362
+ }
363
+ return data;
364
+ }
365
+ };
366
+
367
+ export { MastraAuthNeon, MastraRBACNeon, mapNeonUserToEEUser };
368
+ //# sourceMappingURL=index.js.map
369
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rbac-provider.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Neon Auth RBAC provider for Mastra.\n *\n * Maps Neon Auth organization roles (via Better Auth's Organization plugin)\n * to Mastra permissions using a configurable role mapping.\n */\n\nimport type { IRBACProvider, RoleMapping } from '@mastra/core/auth/ee';\nimport { resolvePermissionsFromMapping, matchesPermission } from '@mastra/core/auth/ee';\n\nimport type { NeonAuthUser } from './index';\n\n/**\n * Response shape from the Neon Auth organization member endpoint.\n */\ninterface NeonOrgMember {\n id: string;\n organizationId: string;\n userId: string;\n role: string;\n createdAt: string;\n}\n\nexport interface NeonRoleMappingOptions {\n /** Cache TTL in milliseconds (default: 60_000) */\n ttlMs?: number;\n /** Max cache entries (default: 1000) */\n maxSize?: number;\n}\n\nexport interface MastraRBACNeonOptions {\n /**\n * Base URL for the Neon Auth service.\n * Falls back to `NEON_AUTH_BASE_URL` env var.\n */\n baseUrl?: string;\n /**\n * Map provider role slugs to arrays of Mastra permission patterns.\n * Use `_default` for roles not explicitly listed.\n *\n * @example\n * ```typescript\n * {\n * owner: ['*'],\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * _default: [],\n * }\n * ```\n */\n roleMapping: RoleMapping;\n /**\n * Specific organization ID to scope role lookups to.\n * If omitted, roles from all organizations are merged.\n */\n organizationId?: string;\n /** Caching options for role lookups. */\n cache?: NeonRoleMappingOptions;\n /**\n * Custom function to extract roles from the NeonAuthUser object.\n * Useful when JWT claims already contain role info.\n */\n getUserRoles?: (user: NeonAuthUser) => string[] | Promise<string[]>;\n}\n\nconst DEFAULT_CACHE_TTL_MS = 60_000;\nconst DEFAULT_CACHE_MAX_SIZE = 1000;\n\ninterface CacheEntry {\n roles: string[];\n expiresAt: number;\n}\n\n/**\n * RBAC provider for Neon Auth that maps organization roles to Mastra permissions.\n *\n * Neon Auth uses Better Auth's Organization plugin which provides `owner`,\n * `admin`, and `member` roles. This provider maps those roles to Mastra\n * permission patterns via a configurable `roleMapping`.\n *\n * @example Static mapping\n * ```typescript\n * import { MastraRBACNeon } from '@mastra/auth-neon';\n *\n * const rbac = new MastraRBACNeon({\n * roleMapping: {\n * owner: ['*'],\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * _default: [],\n * },\n * });\n * ```\n *\n * @example With custom role extraction from JWT claims\n * ```typescript\n * const rbac = new MastraRBACNeon({\n * roleMapping: {\n * admin: ['*'],\n * member: ['agents:read'],\n * },\n * getUserRoles: (user) => {\n * const role = user.jwt?.role as string;\n * return role ? [role] : [];\n * },\n * });\n * ```\n */\nexport class MastraRBACNeon implements IRBACProvider<NeonAuthUser> {\n private options: MastraRBACNeonOptions;\n private baseUrl: string;\n private rolesCache: Map<string, CacheEntry> = new Map();\n private cacheTtlMs: number;\n private cacheMaxSize: number;\n\n get roleMapping(): RoleMapping {\n return this.options.roleMapping;\n }\n\n constructor(options: MastraRBACNeonOptions) {\n this.options = options;\n const rawUrl = options.baseUrl ?? process.env.NEON_AUTH_BASE_URL ?? '';\n let end = rawUrl.length;\n while (end > 0 && rawUrl[end - 1] === '/') {\n end--;\n }\n this.baseUrl = rawUrl.slice(0, end);\n this.cacheTtlMs = options.cache?.ttlMs ?? DEFAULT_CACHE_TTL_MS;\n this.cacheMaxSize = options.cache?.maxSize ?? DEFAULT_CACHE_MAX_SIZE;\n }\n\n async getRoles(user: NeonAuthUser): Promise<string[]> {\n if (this.options.getUserRoles) {\n return this.options.getUserRoles(user);\n }\n\n // Try extracting role from JWT claims first (fast path).\n if (user.jwt) {\n const role = user.jwt.role as string | undefined;\n if (role) return [role];\n }\n\n const userId = user.user?.id;\n if (!userId) return [];\n\n // Check cache (promote on hit for LRU eviction).\n const cached = this.rolesCache.get(userId);\n if (cached && cached.expiresAt > Date.now()) {\n this.rolesCache.delete(userId);\n this.rolesCache.set(userId, cached);\n return cached.roles;\n }\n\n // Fetch organization memberships from Neon Auth.\n const roles = await this.fetchRolesFromNeonAuth(userId);\n\n // Cache the result.\n if (this.rolesCache.size >= this.cacheMaxSize) {\n const firstKey = this.rolesCache.keys().next().value;\n if (firstKey) this.rolesCache.delete(firstKey);\n }\n this.rolesCache.set(userId, { roles, expiresAt: Date.now() + this.cacheTtlMs });\n\n return roles;\n }\n\n async hasRole(user: NeonAuthUser, role: string): Promise<boolean> {\n const roles = await this.getRoles(user);\n return roles.includes(role);\n }\n\n async getPermissions(user: NeonAuthUser): Promise<string[]> {\n const roles = await this.getRoles(user);\n return resolvePermissionsFromMapping(roles, this.options.roleMapping);\n }\n\n async hasPermission(user: NeonAuthUser, 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: NeonAuthUser, 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: NeonAuthUser, 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 async getAvailableRoles(): Promise<{ id: string; name: string }[]> {\n return Object.keys(this.options.roleMapping)\n .filter(k => k !== '_default')\n .map(k => ({ id: k, name: k.charAt(0).toUpperCase() + k.slice(1) }));\n }\n\n async getRolePermissions(roleId: string): Promise<string[]> {\n return resolvePermissionsFromMapping([roleId], this.options.roleMapping);\n }\n\n /**\n * Fetch organization membership roles from Neon Auth.\n *\n * Uses Better Auth's `organization/list-memberships` admin API.\n * Falls back to empty roles on error (default permissions will apply).\n */\n private async fetchRolesFromNeonAuth(userId: string): Promise<string[]> {\n if (!this.baseUrl) return [];\n\n try {\n const response = await fetch(`${this.baseUrl}/auth/api/organization/list-memberships`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ userId }),\n });\n\n if (!response.ok) return [];\n\n const data = (await response.json()) as NeonOrgMember[] | { data?: NeonOrgMember[] };\n const memberships = Array.isArray(data) ? data : (data.data ?? []);\n\n const relevant = this.options.organizationId\n ? memberships.filter(m => m.organizationId === this.options.organizationId)\n : memberships;\n\n return relevant.map(m => m.role);\n } catch {\n return [];\n }\n }\n}\n","import type {\n IUserProvider,\n ICredentialsProvider,\n ISessionProvider,\n Session,\n CredentialsResult,\n} from '@mastra/core/auth';\nimport type { EEUser } from '@mastra/core/auth/ee';\nimport type { MastraAuthProviderOptions } from '@mastra/core/server';\nimport { MastraAuthProvider } from '@mastra/core/server';\n\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\nimport type { JWTPayload } from 'jose';\n\nexport { MastraRBACNeon } from './rbac-provider';\nexport type { MastraRBACNeonOptions, NeonRoleMappingOptions } from './rbac-provider';\n\ntype HonoRequestLike = {\n raw?: Request;\n headers?: Headers;\n header(name: string): string | undefined;\n};\n\ntype MastraAuthRequest = Request | HonoRequestLike;\n\nfunction getRequestHeader(request: MastraAuthRequest, name: string): string | null {\n if (request instanceof Request) {\n return request.headers.get(name);\n }\n return request.raw?.headers.get(name) ?? request.headers?.get(name) ?? request.header(name) ?? null;\n}\n\nfunction stripTrailingSlashes(url: string): string {\n let end = url.length;\n while (end > 0 && url[end - 1] === '/') {\n end--;\n }\n return url.slice(0, end);\n}\n\nfunction parseCookies(response: Response): string[] {\n if (typeof response.headers.getSetCookie === 'function') {\n return response.headers.getSetCookie();\n }\n const raw = response.headers.get('set-cookie');\n if (!raw) return [];\n return raw.split(/,(?=\\s*\\w+=)/);\n}\n\n/**\n * Response shape from Neon Auth session endpoint.\n */\nexport interface NeonSessionResponse {\n session: {\n id: string;\n token: string;\n userId: string;\n expiresAt: string;\n createdAt: string;\n updatedAt: string;\n };\n user: {\n id: string;\n email: string;\n name: string;\n image?: string | null;\n emailVerified: boolean;\n createdAt: string;\n updatedAt: string;\n };\n}\n\n/**\n * User type returned by the adapter's authenticateToken.\n * Contains the session and user data from Neon Auth,\n * plus optional JWT claims when authenticated via bearer JWT.\n */\nexport interface NeonAuthUser {\n session?: NeonSessionResponse['session'];\n user: NeonSessionResponse['user'];\n jwt?: JWTPayload;\n}\n\nexport function mapNeonUserToEEUser(user: NeonSessionResponse['user']): EEUser {\n return {\n id: user.id,\n email: user.email,\n name: user.name,\n avatarUrl: user.image ?? undefined,\n metadata: {\n emailVerified: user.emailVerified,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt,\n },\n };\n}\n\nexport interface MastraAuthNeonOptions extends MastraAuthProviderOptions<NeonAuthUser> {\n /**\n * The Neon Auth base URL (e.g., `https://your-project.neon.tech`).\n * Falls back to the `NEON_AUTH_BASE_URL` environment variable.\n */\n baseUrl?: string;\n /**\n * Explicit JWKS URL for JWT token verification.\n * Overrides the URL derived from `baseUrl`.\n * Falls back to the `NEON_AUTH_JWKS_URL` environment variable.\n */\n jwksUrl?: string;\n /**\n * The session cookie name used by Neon Auth.\n * @default 'neonauth.session_token'\n */\n sessionCookieName?: string;\n /**\n * Whether to allow new user registration via sign-up.\n * @default true\n */\n signUpEnabled?: boolean;\n}\n\n/**\n * Mastra authentication provider for Neon Auth.\n *\n * Neon Auth is a managed authentication service built on Better Auth\n * that stores users, sessions, and auth configuration directly in your\n * Neon Postgres database.\n *\n * This adapter supports:\n * - JWT bearer token verification via JWKS (for API clients)\n * - Session cookie verification via Neon Auth REST API (for Studio)\n * - Email/password sign-in and sign-up (for Studio credentials flow)\n * - Session management (validate, refresh, destroy)\n *\n * @example\n * ```typescript\n * import { MastraAuthNeon } from '@mastra/auth-neon';\n *\n * const auth = new MastraAuthNeon({\n * baseUrl: process.env.NEON_AUTH_BASE_URL,\n * });\n *\n * const mastra = new Mastra({\n * server: {\n * auth,\n * },\n * });\n * ```\n *\n * @example With RBAC\n * ```typescript\n * import { MastraAuthNeon, MastraRBACNeon } from '@mastra/auth-neon';\n *\n * const mastra = new Mastra({\n * server: {\n * auth: new MastraAuthNeon({\n * baseUrl: process.env.NEON_AUTH_BASE_URL,\n * }),\n * rbac: new MastraRBACNeon({\n * roleMapping: {\n * owner: ['*'],\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * _default: [],\n * },\n * }),\n * },\n * });\n * ```\n *\n * @see https://neon.com/docs/auth/overview\n */\nexport class MastraAuthNeon\n extends MastraAuthProvider<NeonAuthUser>\n implements IUserProvider<EEUser>, ICredentialsProvider<EEUser>, ISessionProvider<Session>\n{\n protected baseUrl: string;\n protected jwksUrl: string;\n public sessionCookieName: string;\n protected signUpEnabledConfig: boolean;\n\n constructor(options?: MastraAuthNeonOptions) {\n super({ name: options?.name ?? 'neon' });\n\n const baseUrl = options?.baseUrl ?? process.env.NEON_AUTH_BASE_URL;\n\n if (!baseUrl) {\n throw new Error(\n 'Neon Auth base URL is required, please provide it in the options or set the NEON_AUTH_BASE_URL environment variable',\n );\n }\n\n this.baseUrl = stripTrailingSlashes(baseUrl);\n this.jwksUrl = options?.jwksUrl ?? process.env.NEON_AUTH_JWKS_URL ?? `${this.baseUrl}/auth/jwks`;\n this.sessionCookieName = options?.sessionCookieName ?? 'neonauth.session_token';\n this.signUpEnabledConfig = options?.signUpEnabled ?? true;\n\n this.registerOptions(options);\n }\n\n /** Expose the base URL for RBAC or other consumers. */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n isSignUpEnabled(): boolean {\n return this.signUpEnabledConfig;\n }\n\n // ── IUserProvider ──\n\n async getCurrentUser(request: Request): Promise<EEUser | null> {\n try {\n const result = await this.fetchSession(request.headers);\n if (!result?.user) return null;\n return mapNeonUserToEEUser(result.user);\n } catch {\n return null;\n }\n }\n\n async getUser(_userId: string): Promise<EEUser | null> {\n return null;\n }\n\n getUserProfileUrl(user: EEUser): string {\n return `/profile/${user.id}`;\n }\n\n // ── MastraAuthProvider ──\n\n async authenticateToken(token: string, request: MastraAuthRequest): Promise<NeonAuthUser | null> {\n if (!token || typeof token !== 'string') {\n return null;\n }\n\n // Try JWT verification first (for bearer JWT tokens from API clients).\n const jwtResult = await this.verifyJwt(token);\n if (jwtResult) {\n return {\n user: {\n id: jwtResult.sub ?? '',\n email: (jwtResult.email as string) ?? '',\n name: (jwtResult.name as string) ?? '',\n image: (jwtResult.picture as string) ?? null,\n emailVerified: (jwtResult.email_verified as boolean) ?? false,\n createdAt: jwtResult.iat ? new Date(jwtResult.iat * 1000).toISOString() : '',\n updatedAt: '',\n },\n jwt: jwtResult,\n };\n }\n\n // Fall back to session cookie verification via Neon Auth API.\n try {\n const headers = new Headers();\n\n const cookieHeader = getRequestHeader(request, 'Cookie');\n if (cookieHeader) {\n headers.set('Cookie', cookieHeader);\n }\n\n const hasSessionCookie = !!cookieHeader\n ?.split(';')\n .some(pair => pair.trim().split('=')[0]?.trim() === this.sessionCookieName);\n\n if (token && !hasSessionCookie) {\n const existingCookies = cookieHeader ? `${cookieHeader}; ` : '';\n headers.set('Cookie', `${existingCookies}${this.sessionCookieName}=${token}`);\n }\n\n const result = await this.fetchSession(headers);\n if (!result?.session || !result?.user) {\n return null;\n }\n\n return {\n session: result.session,\n user: result.user,\n };\n } catch {\n return null;\n }\n }\n\n async authorizeUser(user: NeonAuthUser): Promise<boolean> {\n if (!user?.user?.id) return false;\n\n if (user.jwt?.exp && user.jwt.exp * 1000 < Date.now()) {\n return false;\n }\n\n return true;\n }\n\n // ── ICredentialsProvider ──\n\n async signIn(email: string, password: string, request: Request): Promise<CredentialsResult<EEUser>> {\n const response = await fetch(`${this.baseUrl}/auth/sign-in/email`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(request?.headers ? Object.fromEntries(request.headers.entries()) : {}),\n },\n body: JSON.stringify({ email, password }),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as { message?: string };\n throw new Error(errorData.message || 'Invalid email or password');\n }\n\n const result = (await response.json()) as { user?: NeonSessionResponse['user']; token?: string | null };\n\n if (!result?.user) {\n throw new Error('Invalid email or password');\n }\n\n const cookies = parseCookies(response);\n\n return {\n user: mapNeonUserToEEUser(result.user),\n token: result.token ?? undefined,\n cookies,\n };\n }\n\n async signUp(\n email: string,\n password: string,\n name: string | undefined,\n request: Request,\n ): Promise<CredentialsResult<EEUser>> {\n const displayName = name ?? email.split('@')[0] ?? 'User';\n\n const response = await fetch(`${this.baseUrl}/auth/sign-up/email`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(request?.headers ? Object.fromEntries(request.headers.entries()) : {}),\n },\n body: JSON.stringify({ email, password, name: displayName }),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as { message?: string };\n throw new Error(errorData.message || 'Failed to create account');\n }\n\n const result = (await response.json()) as { user?: NeonSessionResponse['user']; token?: string | null };\n\n if (!result?.user) {\n throw new Error('Failed to create account');\n }\n\n const cookies = parseCookies(response);\n\n return {\n user: mapNeonUserToEEUser(result.user),\n token: result.token ?? undefined,\n cookies,\n };\n }\n\n // ── ISessionProvider ──\n\n async createSession(_userId: string, metadata?: Record<string, unknown>): Promise<Session> {\n const now = new Date();\n const expiresAt = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);\n return {\n id: crypto.randomUUID(),\n userId: _userId,\n createdAt: now,\n expiresAt,\n metadata,\n };\n }\n\n async validateSession(sessionId: string): Promise<Session | null> {\n try {\n const headers = new Headers();\n headers.set('Cookie', `${this.sessionCookieName}=${sessionId}`);\n const result = await this.fetchSession(headers);\n if (!result?.session) return null;\n\n return {\n id: result.session.id,\n userId: result.session.userId,\n expiresAt: new Date(result.session.expiresAt),\n createdAt: new Date(result.session.createdAt),\n };\n } catch {\n return null;\n }\n }\n\n async destroySession(_sessionId: string): Promise<void> {\n // Neon Auth (Better Auth) manages session destruction via the sign-out endpoint.\n // Cookie clearing happens via getClearSessionHeaders.\n }\n\n async refreshSession(sessionId: string): Promise<Session | null> {\n // Neon Auth (Better Auth) refreshes sessions automatically when\n // get-session is called and the updateAge threshold is reached.\n return this.validateSession(sessionId);\n }\n\n getSessionIdFromRequest(request: Request): string | null {\n const cookieHeader = request.headers.get('Cookie');\n if (!cookieHeader) return null;\n\n for (const pair of cookieHeader.split(';')) {\n const [key, ...rest] = pair.trim().split('=');\n if (key?.trim() === this.sessionCookieName) {\n return rest.join('=') || null;\n }\n }\n\n return null;\n }\n\n getSessionHeaders(session: Session): Record<string, string> {\n const cookie = (session as unknown as Record<string, unknown>)._sessionCookie;\n if (typeof cookie === 'string') {\n return { 'Set-Cookie': cookie };\n }\n return {};\n }\n\n getClearSessionHeaders(): Record<string, string> {\n const cookies = [\n `${this.sessionCookieName}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`,\n `${this.sessionCookieName}_sig=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`,\n ];\n return {\n 'Set-Cookie': cookies.join(', '),\n };\n }\n\n // ── Internal helpers ──\n\n private async verifyJwt(token: string): Promise<JWTPayload | null> {\n try {\n const JWKS = createRemoteJWKSet(new URL(this.jwksUrl));\n const { payload } = await jwtVerify(token, JWKS);\n return payload;\n } catch {\n return null;\n }\n }\n\n /** Fetch and validate a session from the Neon Auth REST API. */\n public async fetchSession(headers: Headers): Promise<NeonSessionResponse | null> {\n const response = await fetch(`${this.baseUrl}/auth/get-session`, {\n method: 'GET',\n headers,\n });\n\n if (!response.ok) {\n return null;\n }\n\n const data = (await response.json()) as NeonSessionResponse | null;\n if (!data?.session || !data?.user) {\n return null;\n }\n\n return data;\n }\n}\n"]}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Neon Auth RBAC provider for Mastra.
3
+ *
4
+ * Maps Neon Auth organization roles (via Better Auth's Organization plugin)
5
+ * to Mastra permissions using a configurable role mapping.
6
+ */
7
+ import type { IRBACProvider, RoleMapping } from '@mastra/core/auth/ee';
8
+ import type { NeonAuthUser } from './index.js';
9
+ export interface NeonRoleMappingOptions {
10
+ /** Cache TTL in milliseconds (default: 60_000) */
11
+ ttlMs?: number;
12
+ /** Max cache entries (default: 1000) */
13
+ maxSize?: number;
14
+ }
15
+ export interface MastraRBACNeonOptions {
16
+ /**
17
+ * Base URL for the Neon Auth service.
18
+ * Falls back to `NEON_AUTH_BASE_URL` env var.
19
+ */
20
+ baseUrl?: string;
21
+ /**
22
+ * Map provider role slugs to arrays of Mastra permission patterns.
23
+ * Use `_default` for roles not explicitly listed.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * {
28
+ * owner: ['*'],
29
+ * admin: ['*'],
30
+ * member: ['agents:read', 'workflows:*'],
31
+ * _default: [],
32
+ * }
33
+ * ```
34
+ */
35
+ roleMapping: RoleMapping;
36
+ /**
37
+ * Specific organization ID to scope role lookups to.
38
+ * If omitted, roles from all organizations are merged.
39
+ */
40
+ organizationId?: string;
41
+ /** Caching options for role lookups. */
42
+ cache?: NeonRoleMappingOptions;
43
+ /**
44
+ * Custom function to extract roles from the NeonAuthUser object.
45
+ * Useful when JWT claims already contain role info.
46
+ */
47
+ getUserRoles?: (user: NeonAuthUser) => string[] | Promise<string[]>;
48
+ }
49
+ /**
50
+ * RBAC provider for Neon Auth that maps organization roles to Mastra permissions.
51
+ *
52
+ * Neon Auth uses Better Auth's Organization plugin which provides `owner`,
53
+ * `admin`, and `member` roles. This provider maps those roles to Mastra
54
+ * permission patterns via a configurable `roleMapping`.
55
+ *
56
+ * @example Static mapping
57
+ * ```typescript
58
+ * import { MastraRBACNeon } from '@mastra/auth-neon';
59
+ *
60
+ * const rbac = new MastraRBACNeon({
61
+ * roleMapping: {
62
+ * owner: ['*'],
63
+ * admin: ['*'],
64
+ * member: ['agents:read', 'workflows:*'],
65
+ * _default: [],
66
+ * },
67
+ * });
68
+ * ```
69
+ *
70
+ * @example With custom role extraction from JWT claims
71
+ * ```typescript
72
+ * const rbac = new MastraRBACNeon({
73
+ * roleMapping: {
74
+ * admin: ['*'],
75
+ * member: ['agents:read'],
76
+ * },
77
+ * getUserRoles: (user) => {
78
+ * const role = user.jwt?.role as string;
79
+ * return role ? [role] : [];
80
+ * },
81
+ * });
82
+ * ```
83
+ */
84
+ export declare class MastraRBACNeon implements IRBACProvider<NeonAuthUser> {
85
+ private options;
86
+ private baseUrl;
87
+ private rolesCache;
88
+ private cacheTtlMs;
89
+ private cacheMaxSize;
90
+ get roleMapping(): RoleMapping;
91
+ constructor(options: MastraRBACNeonOptions);
92
+ getRoles(user: NeonAuthUser): Promise<string[]>;
93
+ hasRole(user: NeonAuthUser, role: string): Promise<boolean>;
94
+ getPermissions(user: NeonAuthUser): Promise<string[]>;
95
+ hasPermission(user: NeonAuthUser, permission: string): Promise<boolean>;
96
+ hasAllPermissions(user: NeonAuthUser, permissions: string[]): Promise<boolean>;
97
+ hasAnyPermission(user: NeonAuthUser, permissions: string[]): Promise<boolean>;
98
+ getAvailableRoles(): Promise<{
99
+ id: string;
100
+ name: string;
101
+ }[]>;
102
+ getRolePermissions(roleId: string): Promise<string[]>;
103
+ /**
104
+ * Fetch organization membership roles from Neon Auth.
105
+ *
106
+ * Uses Better Auth's `organization/list-memberships` admin API.
107
+ * Falls back to empty roles on error (default permissions will apply).
108
+ */
109
+ private fetchRolesFromNeonAuth;
110
+ }
111
+ //# sourceMappingURL=rbac-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbac-provider.d.ts","sourceRoot":"","sources":["../src/rbac-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAa5C,MAAM,WAAW,sBAAsB;IACrC,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;;;OAaG;IACH,WAAW,EAAE,WAAW,CAAC;IACzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACrE;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,cAAe,YAAW,aAAa,CAAC,YAAY,CAAC;IAChE,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAS;IAE7B,IAAI,WAAW,IAAI,WAAW,CAE7B;gBAEW,OAAO,EAAE,qBAAqB;IAYpC,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmC/C,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK3D,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKrD,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKvE,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAK9E,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAK7E,iBAAiB,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAM5D,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI3D;;;;;OAKG;YACW,sBAAsB;CAwBrC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/auth-neon",
3
- "version": "0.0.0",
3
+ "version": "0.2.0",
4
4
  "description": "Mastra Neon Auth integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,12 +18,6 @@
18
18
  },
19
19
  "./package.json": "./package.json"
20
20
  },
21
- "scripts": {
22
- "build": "tsup --silent --config tsup.config.ts",
23
- "build:watch": "tsup --watch --silent --config tsup.config.ts",
24
- "test": "vitest run",
25
- "lint": "eslint ."
26
- },
27
21
  "license": "Apache-2.0",
28
22
  "dependencies": {
29
23
  "jose": "^6.2.1"
@@ -32,16 +26,16 @@
32
26
  "@mastra/core": ">=1.0.0-0 <2.0.0-0"
33
27
  },
34
28
  "devDependencies": {
35
- "@internal/lint": "workspace:*",
36
- "@internal/types-builder": "workspace:*",
37
- "@mastra/core": "workspace:*",
38
29
  "@types/node": "22.19.21",
39
- "@vitest/coverage-v8": "catalog:",
40
- "@vitest/ui": "catalog:",
30
+ "@vitest/coverage-v8": "4.1.8",
31
+ "@vitest/ui": "4.1.8",
41
32
  "eslint": "^10.4.1",
42
33
  "tsup": "^8.5.1",
43
- "typescript": "catalog:",
44
- "vitest": "catalog:"
34
+ "typescript": "^6.0.3",
35
+ "vitest": "4.1.8",
36
+ "@internal/lint": "0.0.105",
37
+ "@internal/types-builder": "0.0.80",
38
+ "@mastra/core": "1.43.0"
45
39
  },
46
40
  "files": [
47
41
  "dist",
@@ -58,5 +52,11 @@
58
52
  },
59
53
  "engines": {
60
54
  "node": ">=22.13.0"
55
+ },
56
+ "scripts": {
57
+ "build": "tsup --silent --config tsup.config.ts",
58
+ "build:watch": "tsup --watch --silent --config tsup.config.ts",
59
+ "test": "vitest run",
60
+ "lint": "eslint ."
61
61
  }
62
- }
62
+ }