@mastra/auth-firebase 0.10.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.
@@ -0,0 +1 @@
1
+ export { MastraAuthFirebase } from './_tsup-dts-rollup.cjs';
@@ -0,0 +1 @@
1
+ export { MastraAuthFirebase } from './_tsup-dts-rollup.js';
package/dist/index.js ADDED
@@ -0,0 +1,356 @@
1
+ import { SpanKind, trace, context, propagation, SpanStatusCode } from './chunk-JLXWUSDO.js';
2
+ import admin from 'firebase-admin';
3
+ import { getFirestore } from 'firebase-admin/firestore';
4
+
5
+ // ../../packages/core/dist/chunk-EWDGXKOQ.js
6
+ function hasActiveTelemetry(tracerName = "default-tracer") {
7
+ try {
8
+ return !!trace.getTracer(tracerName);
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+ function getBaggageValues(ctx) {
14
+ const currentBaggage = propagation.getBaggage(ctx);
15
+ const requestId = currentBaggage?.getEntry("http.request_id")?.value;
16
+ const componentName = currentBaggage?.getEntry("componentName")?.value;
17
+ const runId = currentBaggage?.getEntry("runId")?.value;
18
+ return {
19
+ requestId,
20
+ componentName,
21
+ runId
22
+ };
23
+ }
24
+ function withSpan(options) {
25
+ return function(_target, propertyKey, descriptor) {
26
+ if (!descriptor || typeof descriptor === "number") return;
27
+ const originalMethod = descriptor.value;
28
+ const methodName = String(propertyKey);
29
+ descriptor.value = function(...args) {
30
+ if (options?.skipIfNoTelemetry && !hasActiveTelemetry(options?.tracerName)) {
31
+ return originalMethod.apply(this, args);
32
+ }
33
+ const tracer = trace.getTracer(options?.tracerName ?? "default-tracer");
34
+ let spanName;
35
+ let spanKind;
36
+ if (typeof options === "string") {
37
+ spanName = options;
38
+ } else if (options) {
39
+ spanName = options.spanName || methodName;
40
+ spanKind = options.spanKind;
41
+ } else {
42
+ spanName = methodName;
43
+ }
44
+ const span = tracer.startSpan(spanName, { kind: spanKind });
45
+ let ctx = trace.setSpan(context.active(), span);
46
+ args.forEach((arg, index) => {
47
+ try {
48
+ span.setAttribute(`${spanName}.argument.${index}`, JSON.stringify(arg));
49
+ } catch {
50
+ span.setAttribute(`${spanName}.argument.${index}`, "[Not Serializable]");
51
+ }
52
+ });
53
+ const { requestId, componentName, runId } = getBaggageValues(ctx);
54
+ if (requestId) {
55
+ span.setAttribute("http.request_id", requestId);
56
+ }
57
+ if (componentName) {
58
+ span.setAttribute("componentName", componentName);
59
+ span.setAttribute("runId", runId);
60
+ } else if (this && this.name) {
61
+ span.setAttribute("componentName", this.name);
62
+ span.setAttribute("runId", this.runId);
63
+ ctx = propagation.setBaggage(
64
+ ctx,
65
+ propagation.createBaggage({
66
+ // @ts-ignore
67
+ componentName: { value: this.name },
68
+ // @ts-ignore
69
+ runId: { value: this.runId },
70
+ // @ts-ignore
71
+ "http.request_id": { value: requestId }
72
+ })
73
+ );
74
+ }
75
+ let result;
76
+ try {
77
+ result = context.with(ctx, () => originalMethod.apply(this, args));
78
+ if (result instanceof Promise) {
79
+ return result.then((resolvedValue) => {
80
+ try {
81
+ span.setAttribute(`${spanName}.result`, JSON.stringify(resolvedValue));
82
+ } catch {
83
+ span.setAttribute(`${spanName}.result`, "[Not Serializable]");
84
+ }
85
+ return resolvedValue;
86
+ }).finally(() => span.end());
87
+ }
88
+ try {
89
+ span.setAttribute(`${spanName}.result`, JSON.stringify(result));
90
+ } catch {
91
+ span.setAttribute(`${spanName}.result`, "[Not Serializable]");
92
+ }
93
+ return result;
94
+ } catch (error) {
95
+ span.setStatus({
96
+ code: SpanStatusCode.ERROR,
97
+ message: error instanceof Error ? error.message : "Unknown error"
98
+ });
99
+ if (error instanceof Error) {
100
+ span.recordException(error);
101
+ }
102
+ throw error;
103
+ } finally {
104
+ if (!(result instanceof Promise)) {
105
+ span.end();
106
+ }
107
+ }
108
+ };
109
+ return descriptor;
110
+ };
111
+ }
112
+ function InstrumentClass(options) {
113
+ return function(target) {
114
+ const methods = Object.getOwnPropertyNames(target.prototype);
115
+ methods.forEach((method) => {
116
+ if (options?.excludeMethods?.includes(method) || method === "constructor") return;
117
+ if (options?.methodFilter && !options.methodFilter(method)) return;
118
+ const descriptor = Object.getOwnPropertyDescriptor(target.prototype, method);
119
+ if (descriptor && typeof descriptor.value === "function") {
120
+ Object.defineProperty(
121
+ target.prototype,
122
+ method,
123
+ withSpan({
124
+ spanName: options?.prefix ? `${options.prefix}.${method}` : method,
125
+ skipIfNoTelemetry: true,
126
+ spanKind: options?.spanKind || SpanKind.INTERNAL,
127
+ tracerName: options?.tracerName
128
+ })(target, method, descriptor)
129
+ );
130
+ }
131
+ });
132
+ return target;
133
+ };
134
+ }
135
+
136
+ // ../../packages/core/dist/chunk-XXVGT7SJ.js
137
+ var RegisteredLogger = {
138
+ LLM: "LLM"};
139
+ var LogLevel = {
140
+ DEBUG: "debug",
141
+ INFO: "info",
142
+ WARN: "warn",
143
+ ERROR: "error"};
144
+ var MastraLogger = class {
145
+ name;
146
+ level;
147
+ transports;
148
+ constructor(options = {}) {
149
+ this.name = options.name || "Mastra";
150
+ this.level = options.level || LogLevel.ERROR;
151
+ this.transports = new Map(Object.entries(options.transports || {}));
152
+ }
153
+ getTransports() {
154
+ return this.transports;
155
+ }
156
+ async getLogs(transportId) {
157
+ if (!transportId || !this.transports.has(transportId)) {
158
+ return [];
159
+ }
160
+ return this.transports.get(transportId).getLogs() ?? [];
161
+ }
162
+ async getLogsByRunId({ transportId, runId }) {
163
+ if (!transportId || !this.transports.has(transportId) || !runId) {
164
+ return [];
165
+ }
166
+ return this.transports.get(transportId).getLogsByRunId({ runId }) ?? [];
167
+ }
168
+ };
169
+ var ConsoleLogger = class extends MastraLogger {
170
+ constructor(options = {}) {
171
+ super(options);
172
+ }
173
+ debug(message, ...args) {
174
+ if (this.level === LogLevel.DEBUG) {
175
+ console.debug(message, ...args);
176
+ }
177
+ }
178
+ info(message, ...args) {
179
+ if (this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) {
180
+ console.info(message, ...args);
181
+ }
182
+ }
183
+ warn(message, ...args) {
184
+ if (this.level === LogLevel.WARN || this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) {
185
+ console.warn(message, ...args);
186
+ }
187
+ }
188
+ error(message, ...args) {
189
+ if (this.level === LogLevel.ERROR || this.level === LogLevel.WARN || this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) {
190
+ console.error(message, ...args);
191
+ }
192
+ }
193
+ async getLogs(_transportId) {
194
+ return [];
195
+ }
196
+ async getLogsByRunId(_args) {
197
+ return [];
198
+ }
199
+ };
200
+
201
+ // ../../packages/core/dist/chunk-JOCKZ2US.js
202
+ var MastraBase = class {
203
+ component = RegisteredLogger.LLM;
204
+ logger;
205
+ name;
206
+ telemetry;
207
+ constructor({ component, name }) {
208
+ this.component = component || RegisteredLogger.LLM;
209
+ this.name = name;
210
+ this.logger = new ConsoleLogger({ name: `${this.component} - ${this.name}` });
211
+ }
212
+ /**
213
+ * Set the logger for the agent
214
+ * @param logger
215
+ */
216
+ __setLogger(logger) {
217
+ this.logger = logger;
218
+ if (this.component !== RegisteredLogger.LLM) {
219
+ this.logger.debug(`Logger updated [component=${this.component}] [name=${this.name}]`);
220
+ }
221
+ }
222
+ /**
223
+ * Set the telemetry for the
224
+ * @param telemetry
225
+ */
226
+ __setTelemetry(telemetry) {
227
+ this.telemetry = telemetry;
228
+ if (this.component !== RegisteredLogger.LLM) {
229
+ this.logger.debug(`Telemetry updated [component=${this.component}] [name=${this.telemetry.name}]`);
230
+ }
231
+ }
232
+ /**
233
+ * Get the telemetry on the vector
234
+ * @returns telemetry
235
+ */
236
+ __getTelemetry() {
237
+ return this.telemetry;
238
+ }
239
+ /*
240
+ get experimental_telemetry config
241
+ */
242
+ get experimental_telemetry() {
243
+ return this.telemetry ? {
244
+ // tracer: this.telemetry.tracer,
245
+ tracer: this.telemetry.getBaggageTracer(),
246
+ isEnabled: !!this.telemetry.tracer
247
+ } : void 0;
248
+ }
249
+ };
250
+
251
+ // ../../packages/core/dist/chunk-C6A6W6XS.js
252
+ var __create = Object.create;
253
+ var __defProp = Object.defineProperty;
254
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
255
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
256
+ var __typeError = (msg) => {
257
+ throw TypeError(msg);
258
+ };
259
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
260
+ enumerable: true,
261
+ configurable: true,
262
+ writable: true,
263
+ value
264
+ }) : obj[key] = value;
265
+ var __name = (target, value) => __defProp(target, "name", {
266
+ value,
267
+ configurable: true
268
+ });
269
+ var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
270
+ var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
271
+ var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
272
+ var __decoratorContext = (kind, name, done, metadata, fns) => ({
273
+ kind: __decoratorStrings[kind],
274
+ name,
275
+ metadata,
276
+ addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
277
+ });
278
+ var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
279
+ var __runInitializers = (array, flags, self, value) => {
280
+ for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) fns[i].call(self) ;
281
+ return value;
282
+ };
283
+ var __decorateElement = (array, flags, name, decorators, target, extra) => {
284
+ var it, done, ctx, k = flags & 7, p = false;
285
+ var j = 0;
286
+ var extraInitializers = array[j] || (array[j] = []);
287
+ var desc = k && ((target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(target , name));
288
+ __name(target, name);
289
+ for (var i = decorators.length - 1; i >= 0; i--) {
290
+ ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
291
+ it = (0, decorators[i])(target, ctx), done._ = 1;
292
+ __expectFn(it) && (target = it);
293
+ }
294
+ return __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
295
+ };
296
+
297
+ // ../../packages/core/dist/server/index.js
298
+ var _MastraAuthProvider_decorators;
299
+ var _init;
300
+ var _a;
301
+ _MastraAuthProvider_decorators = [InstrumentClass({
302
+ prefix: "auth",
303
+ excludeMethods: ["__setTools", "__setLogger", "__setTelemetry", "#log"]
304
+ })];
305
+ var MastraAuthProvider = class extends (_a = MastraBase) {
306
+ constructor(options) {
307
+ super({
308
+ component: "AUTH",
309
+ name: options?.name
310
+ });
311
+ if (options?.authorizeUser) {
312
+ this.authorizeUser = options.authorizeUser.bind(this);
313
+ }
314
+ }
315
+ registerOptions(opts) {
316
+ if (opts?.authorizeUser) {
317
+ this.authorizeUser = opts.authorizeUser.bind(this);
318
+ }
319
+ }
320
+ };
321
+ MastraAuthProvider = /* @__PURE__ */ ((_) => {
322
+ _init = __decoratorStart(_a);
323
+ MastraAuthProvider = __decorateElement(_init, 0, "MastraAuthProvider", _MastraAuthProvider_decorators, MastraAuthProvider);
324
+ __runInitializers(_init, 1, MastraAuthProvider);
325
+ return MastraAuthProvider;
326
+ })();
327
+ var MastraAuthFirebase = class extends MastraAuthProvider {
328
+ serviceAccount;
329
+ databaseId;
330
+ constructor(options) {
331
+ super({ name: options?.name ?? "firebase" });
332
+ this.serviceAccount = options?.serviceAccount ?? process.env.FIREBASE_SERVICE_ACCOUNT;
333
+ this.databaseId = options?.databaseId ?? process.env.FIRESTORE_DATABASE_ID ?? process.env.FIREBASE_DATABASE_ID;
334
+ if (!admin.apps.length) {
335
+ admin.initializeApp({
336
+ credential: this.serviceAccount ? admin.credential.cert(this.serviceAccount) : admin.credential.applicationDefault()
337
+ });
338
+ }
339
+ this.registerOptions(options);
340
+ }
341
+ async authenticateToken(token) {
342
+ const decoded = await admin.auth().verifyIdToken(token);
343
+ return decoded;
344
+ }
345
+ async authorizeUser(user) {
346
+ const db = this.databaseId ? getFirestore(this.databaseId) : getFirestore();
347
+ const userAccess = await db.doc(`/user_access/${user.uid}`).get();
348
+ const userAccessData = userAccess.data();
349
+ if (!userAccessData) {
350
+ return false;
351
+ }
352
+ return true;
353
+ }
354
+ };
355
+
356
+ export { MastraAuthFirebase };
@@ -0,0 +1,6 @@
1
+ import { createConfig } from '@internal/lint/eslint';
2
+
3
+ const config = await createConfig();
4
+
5
+ /** @type {import("eslint").Linter.Config[]} */
6
+ export default [...config];
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@mastra/auth-firebase",
3
+ "version": "0.10.0",
4
+ "description": "Mastra Firebase Auth integration",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "license": "Elastic-2.0",
22
+ "dependencies": {
23
+ "firebase-admin": "^13.4.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.13.1",
27
+ "eslint": "^9.25.1",
28
+ "tsup": "^8.4.0",
29
+ "typescript": "^5.8.3",
30
+ "vitest": "^2.1.9",
31
+ "@internal/lint": "0.0.8",
32
+ "@mastra/core": "0.10.2"
33
+ },
34
+ "scripts": {
35
+ "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake",
36
+ "build:watch": "pnpm build --watch",
37
+ "test": "vitest run",
38
+ "lint": "eslint ."
39
+ }
40
+ }
@@ -0,0 +1,157 @@
1
+ import admin from 'firebase-admin';
2
+ import { getFirestore } from 'firebase-admin/firestore';
3
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
4
+ import { MastraAuthFirebase } from './index';
5
+
6
+ // Mock Firebase Admin
7
+ vi.mock('firebase-admin', () => ({
8
+ default: {
9
+ apps: [],
10
+ initializeApp: vi.fn(),
11
+ auth: vi.fn(() => ({
12
+ verifyIdToken: vi.fn(),
13
+ })),
14
+ credential: {
15
+ cert: vi.fn(() => 'mock-credential'),
16
+ applicationDefault: vi.fn(() => 'mock-default-credential'),
17
+ },
18
+ },
19
+ }));
20
+
21
+ // Mock Firestore
22
+ vi.mock('firebase-admin/firestore', () => ({
23
+ getFirestore: vi.fn(() => ({
24
+ doc: vi.fn(() => ({
25
+ get: vi.fn(),
26
+ })),
27
+ })),
28
+ }));
29
+
30
+ describe('MastraAuthFirebase', () => {
31
+ const mockServiceAccount = 'mock-service-account';
32
+ const mockDatabaseId = 'mock-database-id';
33
+ const mockToken = 'mock-token';
34
+ const mockUserId = 'mock-user-id';
35
+
36
+ beforeEach(() => {
37
+ vi.clearAllMocks();
38
+ });
39
+
40
+ describe('initialization', () => {
41
+ it('should initialize with provided options', () => {
42
+ const auth = new MastraAuthFirebase({
43
+ serviceAccount: mockServiceAccount,
44
+ databaseId: mockDatabaseId,
45
+ });
46
+
47
+ expect(auth).toBeInstanceOf(MastraAuthFirebase);
48
+ expect(admin.initializeApp).toHaveBeenCalledWith({
49
+ credential: 'mock-credential',
50
+ });
51
+ expect(admin.credential.cert).toHaveBeenCalledWith(mockServiceAccount);
52
+ });
53
+
54
+ it('should initialize with environment variables', () => {
55
+ process.env.FIREBASE_SERVICE_ACCOUNT = mockServiceAccount;
56
+ process.env.FIRESTORE_DATABASE_ID = mockDatabaseId;
57
+
58
+ const auth = new MastraAuthFirebase();
59
+
60
+ expect(auth).toBeInstanceOf(MastraAuthFirebase);
61
+ expect(admin.initializeApp).toHaveBeenCalledWith({
62
+ credential: 'mock-credential',
63
+ });
64
+ expect(admin.credential.cert).toHaveBeenCalledWith(mockServiceAccount);
65
+
66
+ delete process.env.FIREBASE_SERVICE_ACCOUNT;
67
+ delete process.env.FIRESTORE_DATABASE_ID;
68
+ });
69
+ });
70
+
71
+ describe('authenticateToken', () => {
72
+ it('should verify and return decoded token', async () => {
73
+ const mockDecodedToken = { uid: mockUserId };
74
+ const mockVerifyIdToken = vi.fn().mockResolvedValue(mockDecodedToken);
75
+
76
+ (admin.auth as any).mockReturnValue({
77
+ verifyIdToken: mockVerifyIdToken,
78
+ });
79
+
80
+ const auth = new MastraAuthFirebase();
81
+ const result = await auth.authenticateToken(mockToken);
82
+
83
+ expect(mockVerifyIdToken).toHaveBeenCalledWith(mockToken);
84
+ expect(result).toEqual(mockDecodedToken);
85
+ });
86
+
87
+ it('should return null when token verification fails', async () => {
88
+ const mockVerifyIdToken = vi.fn().mockRejectedValue(new Error('Invalid token'));
89
+
90
+ (admin.auth as any).mockReturnValue({
91
+ verifyIdToken: mockVerifyIdToken,
92
+ });
93
+
94
+ const auth = new MastraAuthFirebase();
95
+ const result = await auth.authenticateToken(mockToken).catch(() => null);
96
+
97
+ expect(mockVerifyIdToken).toHaveBeenCalledWith(mockToken);
98
+ expect(result).toBeNull();
99
+ });
100
+ });
101
+
102
+ describe('authorizeUser', () => {
103
+ it('should return true when user has access', async () => {
104
+ const mockUser = { uid: mockUserId };
105
+ const mockUserAccessData = { someData: 'value' };
106
+ const mockGet = vi.fn().mockResolvedValue({ data: () => mockUserAccessData });
107
+ const mockDoc = vi.fn().mockReturnValue({ get: mockGet });
108
+
109
+ (getFirestore as any).mockReturnValue({
110
+ doc: mockDoc,
111
+ });
112
+
113
+ const auth = new MastraAuthFirebase();
114
+ const result = await auth.authorizeUser(mockUser as any);
115
+
116
+ expect(mockDoc).toHaveBeenCalledWith(`/user_access/${mockUserId}`);
117
+ expect(result).toBe(true);
118
+ });
119
+
120
+ it('should return false when user has no access', async () => {
121
+ const mockUser = { uid: mockUserId };
122
+ const mockGet = vi.fn().mockResolvedValue({ data: () => null });
123
+ const mockDoc = vi.fn().mockReturnValue({ get: mockGet });
124
+
125
+ (getFirestore as any).mockReturnValue({
126
+ doc: mockDoc,
127
+ });
128
+
129
+ const auth = new MastraAuthFirebase();
130
+ const result = await auth.authorizeUser(mockUser as any);
131
+
132
+ expect(mockDoc).toHaveBeenCalledWith(`/user_access/${mockUserId}`);
133
+ expect(result).toBe(false);
134
+ });
135
+ });
136
+
137
+ it('can be overridden with custom authorization logic', async () => {
138
+ const firebase = new MastraAuthFirebase({
139
+ async authorizeUser(user: any): Promise<boolean> {
140
+ // Custom authorization logic that checks for specific permissions
141
+ return user?.permissions?.includes('admin') ?? false;
142
+ },
143
+ });
144
+
145
+ // Test with admin user
146
+ const adminUser = { sub: 'user123', permissions: ['admin'] } as unknown as DecodedIdToken;
147
+ expect(await firebase.authorizeUser(adminUser)).toBe(true);
148
+
149
+ // Test with non-admin user
150
+ const regularUser = { sub: 'user456', permissions: ['read'] };
151
+ expect(await firebase.authorizeUser(regularUser)).toBe(false);
152
+
153
+ // Test with user without permissions
154
+ const noPermissionsUser = { sub: 'user789' };
155
+ expect(await firebase.authorizeUser(noPermissionsUser)).toBe(false);
156
+ });
157
+ });
package/src/index.ts ADDED
@@ -0,0 +1,51 @@
1
+ import type { MastraAuthProviderOptions } from '@mastra/core/server';
2
+ import { MastraAuthProvider } from '@mastra/core/server';
3
+
4
+ import admin from 'firebase-admin';
5
+ import { getFirestore } from 'firebase-admin/firestore';
6
+
7
+ type FirebaseUser = admin.auth.DecodedIdToken;
8
+
9
+ interface MastraAuthFirebaseOptions extends MastraAuthProviderOptions<FirebaseUser> {
10
+ databaseId?: string;
11
+ serviceAccount?: string;
12
+ }
13
+
14
+ export class MastraAuthFirebase extends MastraAuthProvider<FirebaseUser> {
15
+ private serviceAccount: string | undefined;
16
+ private databaseId: string | undefined;
17
+
18
+ constructor(options?: MastraAuthFirebaseOptions) {
19
+ super({ name: options?.name ?? 'firebase' });
20
+
21
+ this.serviceAccount = options?.serviceAccount ?? process.env.FIREBASE_SERVICE_ACCOUNT;
22
+ this.databaseId = options?.databaseId ?? process.env.FIRESTORE_DATABASE_ID ?? process.env.FIREBASE_DATABASE_ID;
23
+
24
+ if (!admin.apps.length) {
25
+ admin.initializeApp({
26
+ credential: this.serviceAccount
27
+ ? admin.credential.cert(this.serviceAccount)
28
+ : admin.credential.applicationDefault(),
29
+ });
30
+ }
31
+
32
+ this.registerOptions(options);
33
+ }
34
+
35
+ async authenticateToken(token: string): Promise<FirebaseUser | null> {
36
+ const decoded = await admin.auth().verifyIdToken(token);
37
+ return decoded;
38
+ }
39
+
40
+ async authorizeUser(user: FirebaseUser) {
41
+ const db = this.databaseId ? getFirestore(this.databaseId) : getFirestore();
42
+ const userAccess = await db.doc(`/user_access/${user.uid}`).get();
43
+ const userAccessData = userAccess.data();
44
+
45
+ if (!userAccessData) {
46
+ return false;
47
+ }
48
+
49
+ return true;
50
+ }
51
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../tsconfig.node.json",
3
+ "include": ["src/**/*"],
4
+ "exclude": ["node_modules", "**/*.test.ts"]
5
+ }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ include: ['src/**/*.test.ts'],
7
+ },
8
+ });