@forestadmin/mcp-server 0.1.0 → 1.0.1

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.
@@ -1,590 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const forestadmin_client_1 = __importDefault(require("@forestadmin/forestadmin-client"));
7
- const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
8
- const forest_oauth_provider_1 = __importDefault(require("./forest-oauth-provider"));
9
- const mock_server_1 = __importDefault(require("./test-utils/mock-server"));
10
- jest.mock('jsonwebtoken');
11
- jest.mock('@forestadmin/forestadmin-client');
12
- const mockCreateForestAdminClient = forestadmin_client_1.default;
13
- const mockJwtDecode = jsonwebtoken_1.default.decode;
14
- const mockJwtSign = jsonwebtoken_1.default.sign;
15
- const TEST_ENV_SECRET = 'test-env-secret';
16
- const TEST_AUTH_SECRET = 'test-auth-secret';
17
- const TEST_FOREST_APP_URL = 'https://app.forestadmin.com';
18
- function createProvider(forestServerUrl = 'https://api.forestadmin.com') {
19
- return new forest_oauth_provider_1.default({
20
- forestServerUrl,
21
- forestAppUrl: TEST_FOREST_APP_URL,
22
- envSecret: TEST_ENV_SECRET,
23
- authSecret: TEST_AUTH_SECRET,
24
- logger: console.info,
25
- });
26
- }
27
- describe('ForestOAuthProvider', () => {
28
- let originalEnv;
29
- let mockServer;
30
- const originalFetch = global.fetch;
31
- beforeAll(() => {
32
- originalEnv = { ...process.env };
33
- });
34
- afterAll(() => {
35
- process.env = originalEnv;
36
- global.fetch = originalFetch;
37
- });
38
- beforeEach(() => {
39
- process.env.FOREST_ENV_SECRET = TEST_ENV_SECRET;
40
- process.env.FOREST_AUTH_SECRET = TEST_AUTH_SECRET;
41
- mockServer = new mock_server_1.default();
42
- });
43
- afterEach(() => {
44
- mockServer.reset();
45
- });
46
- describe('constructor', () => {
47
- it('should create instance with forestServerUrl', () => {
48
- const customProvider = createProvider('https://custom.forestadmin.com');
49
- expect(customProvider).toBeDefined();
50
- });
51
- it('should create instance with custom forestAppUrl', () => {
52
- const customProvider = new forest_oauth_provider_1.default({
53
- forestServerUrl: 'https://api.forestadmin.com',
54
- forestAppUrl: 'https://custom-app.forestadmin.com',
55
- envSecret: TEST_ENV_SECRET,
56
- authSecret: TEST_AUTH_SECRET,
57
- logger: console.info,
58
- });
59
- expect(customProvider).toBeDefined();
60
- });
61
- });
62
- describe('initialize', () => {
63
- it('should not throw when envSecret is empty string', async () => {
64
- const customProvider = new forest_oauth_provider_1.default({
65
- forestServerUrl: 'https://api.forestadmin.com',
66
- forestAppUrl: TEST_FOREST_APP_URL,
67
- envSecret: '',
68
- authSecret: TEST_AUTH_SECRET,
69
- logger: console.info,
70
- });
71
- await expect(customProvider.initialize()).resolves.not.toThrow();
72
- });
73
- it('should fetch environmentId from Forest Admin API', async () => {
74
- mockServer.get('/liana/environment', {
75
- data: { id: '98765', attributes: { api_endpoint: 'https://api.example.com' } },
76
- });
77
- global.fetch = mockServer.fetch;
78
- const testProvider = createProvider();
79
- await testProvider.initialize();
80
- // Verify fetch was called with correct URL and headers
81
- expect(mockServer.fetch).toHaveBeenCalledWith('https://api.forestadmin.com/liana/environment', expect.objectContaining({
82
- method: 'GET',
83
- headers: expect.objectContaining({
84
- 'forest-secret-key': 'test-env-secret',
85
- 'Content-Type': 'application/json',
86
- }),
87
- }));
88
- });
89
- it('should set environmentId after successful initialization', async () => {
90
- mockServer.get('/liana/environment', {
91
- data: { id: '54321', attributes: { api_endpoint: 'https://api.example.com' } },
92
- });
93
- global.fetch = mockServer.fetch;
94
- const testProvider = createProvider();
95
- await testProvider.initialize();
96
- // Verify environmentId is set by checking authorize redirect includes it
97
- const mockResponse = { redirect: jest.fn() };
98
- const mockClient = {
99
- client_id: 'test-client',
100
- redirect_uris: ['https://example.com/callback'],
101
- };
102
- await testProvider.authorize(mockClient, {
103
- redirectUri: 'https://example.com/callback',
104
- codeChallenge: 'challenge',
105
- state: 'state',
106
- scopes: ['mcp:read'],
107
- resource: new URL('https://localhost:3931'),
108
- }, mockResponse);
109
- const redirectUrl = new URL(mockResponse.redirect.mock.calls[0][0]);
110
- expect(redirectUrl.searchParams.get('environmentId')).toBe('54321');
111
- });
112
- it('should handle non-OK response from Forest Admin API', async () => {
113
- mockServer.get('/liana/environment', { error: 'Unauthorized' }, 401);
114
- global.fetch = mockServer.fetch;
115
- const loggerSpy = jest.fn();
116
- const testProvider = new forest_oauth_provider_1.default({
117
- forestServerUrl: 'https://api.forestadmin.com',
118
- forestAppUrl: TEST_FOREST_APP_URL,
119
- envSecret: TEST_ENV_SECRET,
120
- authSecret: TEST_AUTH_SECRET,
121
- logger: loggerSpy,
122
- });
123
- await testProvider.initialize();
124
- expect(loggerSpy).toHaveBeenCalledWith('Warn', expect.stringContaining('Failed to fetch environmentId from Forest Admin API'));
125
- });
126
- it('should handle fetch network errors gracefully', async () => {
127
- global.fetch = jest.fn().mockRejectedValue(new Error('Network error'));
128
- const loggerSpy = jest.fn();
129
- const testProvider = new forest_oauth_provider_1.default({
130
- forestServerUrl: 'https://api.forestadmin.com',
131
- forestAppUrl: TEST_FOREST_APP_URL,
132
- envSecret: TEST_ENV_SECRET,
133
- authSecret: TEST_AUTH_SECRET,
134
- logger: loggerSpy,
135
- });
136
- await testProvider.initialize();
137
- expect(loggerSpy).toHaveBeenCalledWith('Warn', expect.stringContaining('Failed to fetch environmentId from Forest Admin API'));
138
- });
139
- it('should use correct forest server URL for API call', async () => {
140
- mockServer.get('/liana/environment', {
141
- data: { id: '11111', attributes: { api_endpoint: 'https://api.example.com' } },
142
- });
143
- global.fetch = mockServer.fetch;
144
- const testProvider = createProvider('https://custom.forestadmin.com');
145
- await testProvider.initialize();
146
- expect(mockServer.fetch).toHaveBeenCalledWith('https://custom.forestadmin.com/liana/environment', expect.any(Object));
147
- });
148
- });
149
- describe('clientsStore.getClient', () => {
150
- it('should fetch client from Forest Admin API', async () => {
151
- const clientData = {
152
- client_id: 'test-client-123',
153
- redirect_uris: ['https://example.com/callback'],
154
- client_name: 'Test Client',
155
- };
156
- mockServer.get('/oauth/register/test-client-123', clientData);
157
- global.fetch = mockServer.fetch;
158
- const provider = createProvider();
159
- const client = await provider.clientsStore.getClient('test-client-123');
160
- expect(client).toEqual(clientData);
161
- expect(mockServer.fetch).toHaveBeenCalledWith('https://api.forestadmin.com/oauth/register/test-client-123', expect.objectContaining({
162
- method: 'GET',
163
- headers: expect.objectContaining({
164
- 'Content-Type': 'application/json',
165
- }),
166
- }));
167
- });
168
- it('should return undefined when client is not found', async () => {
169
- mockServer.get('/oauth/register/unknown-client', { error: 'Not found' }, 404);
170
- global.fetch = mockServer.fetch;
171
- const provider = createProvider();
172
- const client = await provider.clientsStore.getClient('unknown-client');
173
- expect(client).toBeUndefined();
174
- });
175
- it('should return undefined on server error', async () => {
176
- mockServer.get('/oauth/register/error-client', { error: 'Internal error' }, 500);
177
- global.fetch = mockServer.fetch;
178
- const provider = createProvider();
179
- const client = await provider.clientsStore.getClient('error-client');
180
- expect(client).toBeUndefined();
181
- });
182
- });
183
- describe('authorize', () => {
184
- let mockResponse;
185
- let mockClient;
186
- let initializedProvider;
187
- beforeEach(async () => {
188
- mockResponse = {
189
- redirect: jest.fn(),
190
- };
191
- mockClient = {
192
- client_id: 'test-client-id',
193
- redirect_uris: ['https://example.com/callback'],
194
- };
195
- // Create provider and mock the fetch to set environmentId
196
- initializedProvider = createProvider();
197
- // Mock fetch to return a valid response
198
- const mockFetch = jest.fn().mockResolvedValue({
199
- ok: true,
200
- json: () => Promise.resolve({
201
- data: { id: '12345', attributes: { api_endpoint: 'https://api.example.com' } },
202
- }),
203
- });
204
- global.fetch = mockFetch;
205
- await initializedProvider.initialize();
206
- });
207
- afterEach(() => {
208
- jest.restoreAllMocks();
209
- });
210
- it('should redirect to Forest Admin authentication URL', async () => {
211
- await initializedProvider.authorize(mockClient, {
212
- redirectUri: 'https://example.com/callback',
213
- codeChallenge: 'test-code-challenge',
214
- state: 'test-state',
215
- scopes: ['mcp:read', 'profile'],
216
- resource: new URL('https://localhost:3931'),
217
- }, mockResponse);
218
- expect(mockResponse.redirect).toHaveBeenCalledWith(expect.stringContaining('https://app.forestadmin.com/oauth/authorize'));
219
- });
220
- it('should include all required query parameters in redirect URL', async () => {
221
- await initializedProvider.authorize(mockClient, {
222
- redirectUri: 'https://example.com/callback',
223
- codeChallenge: 'test-code-challenge',
224
- state: 'test-state',
225
- scopes: ['mcp:read', 'profile'],
226
- resource: new URL('https://localhost:3931'),
227
- }, mockResponse);
228
- const redirectCall = mockResponse.redirect.mock.calls[0][0];
229
- const url = new URL(redirectCall);
230
- expect(url.hostname).toBe('app.forestadmin.com');
231
- expect(url.pathname).toBe('/oauth/authorize');
232
- expect(url.searchParams.get('redirect_uri')).toBe('https://example.com/callback');
233
- expect(url.searchParams.get('code_challenge')).toBe('test-code-challenge');
234
- expect(url.searchParams.get('code_challenge_method')).toBe('S256');
235
- expect(url.searchParams.get('response_type')).toBe('code');
236
- expect(url.searchParams.get('client_id')).toBe('test-client-id');
237
- expect(url.searchParams.get('state')).toBe('test-state');
238
- expect(url.searchParams.get('scope')).toBe('mcp:read+profile');
239
- expect(url.searchParams.get('environmentId')).toBe('12345');
240
- });
241
- it('should redirect to error URL when environmentId is not set', async () => {
242
- // Create a provider without initializing (environmentId is undefined)
243
- const uninitializedProvider = createProvider();
244
- await uninitializedProvider.authorize(mockClient, {
245
- redirectUri: 'https://example.com/callback',
246
- codeChallenge: 'test-code-challenge',
247
- state: 'test-state',
248
- scopes: ['mcp:read'],
249
- resource: new URL('https://localhost:3931'),
250
- }, mockResponse);
251
- const redirectCall = mockResponse.redirect.mock.calls[0][0];
252
- expect(redirectCall).toContain('https://example.com/callback');
253
- expect(redirectCall).toContain('error=server_error');
254
- });
255
- it('should use custom forestAppUrl for redirect', async () => {
256
- const customAppUrl = 'https://custom-app.forestadmin.com';
257
- const customProvider = new forest_oauth_provider_1.default({
258
- forestServerUrl: 'https://api.forestadmin.com',
259
- forestAppUrl: customAppUrl,
260
- envSecret: TEST_ENV_SECRET,
261
- authSecret: TEST_AUTH_SECRET,
262
- logger: console.info,
263
- });
264
- // Mock fetch to return a valid response for initialize
265
- global.fetch = jest.fn().mockResolvedValue({
266
- ok: true,
267
- json: () => Promise.resolve({
268
- data: { id: '12345', attributes: { api_endpoint: 'https://api.example.com' } },
269
- }),
270
- });
271
- await customProvider.initialize();
272
- await customProvider.authorize(mockClient, {
273
- redirectUri: 'https://example.com/callback',
274
- codeChallenge: 'test-code-challenge',
275
- state: 'test-state',
276
- scopes: ['mcp:read'],
277
- resource: new URL('https://localhost:3931'),
278
- }, mockResponse);
279
- const redirectCall = mockResponse.redirect.mock.calls[0][0];
280
- const url = new URL(redirectCall);
281
- expect(url.hostname).toBe('custom-app.forestadmin.com');
282
- expect(url.pathname).toBe('/oauth/authorize');
283
- });
284
- });
285
- describe('exchangeAuthorizationCode', () => {
286
- let mockClient;
287
- let mockGetUserInfo;
288
- beforeEach(() => {
289
- mockClient = {
290
- client_id: 'test-client-id',
291
- redirect_uris: ['https://example.com/callback'],
292
- scope: 'mcp:read mcp:write',
293
- };
294
- // Setup mock for forestAdminClient
295
- mockGetUserInfo = jest.fn().mockResolvedValue({
296
- id: 123,
297
- email: 'user@example.com',
298
- firstName: 'Test',
299
- lastName: 'User',
300
- team: 'Operations',
301
- role: 'Admin',
302
- tags: {},
303
- renderingId: 456,
304
- permissionLevel: 'admin',
305
- });
306
- mockCreateForestAdminClient.mockReturnValue({
307
- authService: {
308
- getUserInfo: mockGetUserInfo,
309
- },
310
- });
311
- // Setup mock for jsonwebtoken - decode is called twice:
312
- // first for access token, then for refresh token
313
- const now = Math.floor(Date.now() / 1000);
314
- mockJwtDecode
315
- .mockReturnValueOnce({
316
- meta: { renderingId: 456 },
317
- exp: now + 3600, // expires in 1 hour
318
- iat: now,
319
- scope: 'mcp:read',
320
- })
321
- .mockReturnValueOnce({
322
- exp: now + 604800, // expires in 7 days
323
- iat: now,
324
- });
325
- mockJwtSign.mockReturnValue('mocked-jwt-token');
326
- });
327
- afterEach(() => {
328
- jest.clearAllMocks();
329
- });
330
- it('should exchange authorization code with Forest Admin server', async () => {
331
- mockServer
332
- .get('/liana/environment', {
333
- data: { id: '12345', attributes: { api_endpoint: 'https://api.example.com' } },
334
- })
335
- .post('/oauth/token', {
336
- access_token: 'forest-access-token',
337
- refresh_token: 'forest-refresh-token',
338
- expires_in: 3600,
339
- token_type: 'Bearer',
340
- scope: 'mcp:read',
341
- });
342
- global.fetch = mockServer.fetch;
343
- const provider = createProvider();
344
- const result = await provider.exchangeAuthorizationCode(mockClient, 'auth-code-123', 'code-verifier-456', 'https://example.com/callback');
345
- expect(mockServer.fetch).toHaveBeenCalledWith('https://api.forestadmin.com/oauth/token', expect.objectContaining({
346
- method: 'POST',
347
- headers: expect.objectContaining({
348
- 'Content-Type': 'application/json',
349
- 'forest-secret-key': 'test-env-secret',
350
- }),
351
- body: JSON.stringify({
352
- grant_type: 'authorization_code',
353
- code: 'auth-code-123',
354
- redirect_uri: 'https://example.com/callback',
355
- client_id: 'test-client-id',
356
- code_verifier: 'code-verifier-456',
357
- }),
358
- }));
359
- expect(result.access_token).toBe('mocked-jwt-token');
360
- expect(result.refresh_token).toBe('mocked-jwt-token');
361
- expect(result.token_type).toBe('Bearer');
362
- // expires_in is calculated as exp - now, so it should be approximately 3600
363
- expect(result.expires_in).toBeGreaterThan(3590);
364
- expect(result.expires_in).toBeLessThanOrEqual(3600);
365
- expect(result.scope).toBe('mcp:read');
366
- expect(mockJwtDecode).toHaveBeenCalledWith('forest-access-token');
367
- expect(mockJwtDecode).toHaveBeenCalledWith('forest-refresh-token');
368
- expect(mockGetUserInfo).toHaveBeenCalledWith(456, 'forest-access-token');
369
- // First call: access token - expiresIn is calculated as exp - now, so it's approximately 3600
370
- expect(mockJwtSign).toHaveBeenCalledWith(expect.objectContaining({
371
- id: 123,
372
- email: 'user@example.com',
373
- serverToken: 'forest-access-token',
374
- }), 'test-auth-secret', { expiresIn: expect.any(Number) });
375
- // Second call: refresh token - expiresIn is calculated as exp - now, so it's approximately 604800
376
- expect(mockJwtSign).toHaveBeenCalledWith(expect.objectContaining({
377
- type: 'refresh',
378
- clientId: 'test-client-id',
379
- userId: 123,
380
- renderingId: 456,
381
- serverRefreshToken: 'forest-refresh-token',
382
- }), 'test-auth-secret', { expiresIn: expect.any(Number) });
383
- });
384
- it('should throw error when token exchange fails', async () => {
385
- mockServer
386
- .get('/liana/environment', {
387
- data: { id: '12345', attributes: { api_endpoint: 'https://api.example.com' } },
388
- })
389
- .post('/oauth/token', { error: 'invalid_grant' }, 400);
390
- global.fetch = mockServer.fetch;
391
- const provider = createProvider();
392
- await expect(provider.exchangeAuthorizationCode(mockClient, 'invalid-code', 'code-verifier', 'https://example.com/callback')).rejects.toThrow('Failed to exchange authorization code');
393
- });
394
- });
395
- describe('exchangeRefreshToken', () => {
396
- let mockClient;
397
- let mockGetUserInfo;
398
- beforeEach(() => {
399
- mockClient = {
400
- client_id: 'test-client-id',
401
- redirect_uris: ['https://example.com/callback'],
402
- scope: 'mcp:read mcp:write',
403
- };
404
- mockGetUserInfo = jest.fn().mockResolvedValue({
405
- id: 123,
406
- email: 'user@example.com',
407
- firstName: 'Test',
408
- lastName: 'User',
409
- team: 'Operations',
410
- role: 'Admin',
411
- tags: {},
412
- renderingId: 456,
413
- permissionLevel: 'admin',
414
- });
415
- mockCreateForestAdminClient.mockReturnValue({
416
- authService: {
417
- getUserInfo: mockGetUserInfo,
418
- },
419
- });
420
- mockJwtSign.mockReturnValue('new-mocked-jwt-token');
421
- });
422
- afterEach(() => {
423
- jest.clearAllMocks();
424
- });
425
- it('should exchange refresh token for new tokens', async () => {
426
- // Mock jwt.verify to return decoded refresh token
427
- jsonwebtoken_1.default.verify.mockReturnValue({
428
- type: 'refresh',
429
- clientId: 'test-client-id',
430
- userId: 123,
431
- renderingId: 456,
432
- serverRefreshToken: 'forest-refresh-token',
433
- });
434
- // Mock jwt.decode - called twice: first for access token, then for refresh token
435
- const now = Math.floor(Date.now() / 1000);
436
- mockJwtDecode
437
- .mockReturnValueOnce({
438
- meta: { renderingId: 456 },
439
- exp: now + 3600, // expires in 1 hour
440
- iat: now,
441
- scope: 'mcp:read',
442
- })
443
- .mockReturnValueOnce({
444
- exp: now + 604800, // expires in 7 days
445
- iat: now,
446
- });
447
- mockServer.post('/oauth/token', {
448
- access_token: 'new-forest-access-token',
449
- refresh_token: 'new-forest-refresh-token',
450
- expires_in: 3600,
451
- token_type: 'Bearer',
452
- scope: 'mcp:read',
453
- });
454
- global.fetch = mockServer.fetch;
455
- const provider = createProvider();
456
- const result = await provider.exchangeRefreshToken(mockClient, 'valid-refresh-token');
457
- expect(result.access_token).toBe('new-mocked-jwt-token');
458
- expect(result.refresh_token).toBe('new-mocked-jwt-token');
459
- expect(result.token_type).toBe('Bearer');
460
- // expires_in is calculated as exp - now, so it should be approximately 3600
461
- expect(result.expires_in).toBeGreaterThan(3590);
462
- expect(result.expires_in).toBeLessThanOrEqual(3600);
463
- expect(mockServer.fetch).toHaveBeenCalledWith('https://api.forestadmin.com/oauth/token', expect.objectContaining({
464
- method: 'POST',
465
- body: JSON.stringify({
466
- grant_type: 'refresh_token',
467
- refresh_token: 'forest-refresh-token',
468
- client_id: 'test-client-id',
469
- }),
470
- }));
471
- });
472
- it('should throw error for invalid refresh token', async () => {
473
- jsonwebtoken_1.default.verify.mockImplementation(() => {
474
- throw new Error('invalid signature');
475
- });
476
- const provider = createProvider();
477
- await expect(provider.exchangeRefreshToken(mockClient, 'invalid-refresh-token')).rejects.toThrow('Invalid or expired refresh token');
478
- });
479
- it('should throw error when token type is not refresh', async () => {
480
- jsonwebtoken_1.default.verify.mockReturnValue({
481
- type: 'access',
482
- clientId: 'test-client-id',
483
- });
484
- const provider = createProvider();
485
- await expect(provider.exchangeRefreshToken(mockClient, 'access-token')).rejects.toThrow('Invalid token type');
486
- });
487
- it('should throw error when client_id does not match', async () => {
488
- jsonwebtoken_1.default.verify.mockReturnValue({
489
- type: 'refresh',
490
- clientId: 'different-client-id',
491
- userId: 123,
492
- renderingId: 456,
493
- serverRefreshToken: 'forest-refresh-token',
494
- });
495
- const provider = createProvider();
496
- await expect(provider.exchangeRefreshToken(mockClient, 'refresh-token-for-different-client')).rejects.toThrow('Token was not issued to this client');
497
- });
498
- it('should throw error when Forest Admin refresh fails', async () => {
499
- jsonwebtoken_1.default.verify.mockReturnValue({
500
- type: 'refresh',
501
- clientId: 'test-client-id',
502
- userId: 123,
503
- renderingId: 456,
504
- serverRefreshToken: 'expired-forest-refresh-token',
505
- });
506
- mockServer.post('/oauth/token', { error: 'invalid_grant' }, 400);
507
- global.fetch = mockServer.fetch;
508
- const provider = createProvider();
509
- await expect(provider.exchangeRefreshToken(mockClient, 'valid-refresh-token')).rejects.toThrow('Failed to refresh token');
510
- });
511
- });
512
- describe('verifyAccessToken', () => {
513
- it('should verify and decode a valid access token', async () => {
514
- const mockDecoded = {
515
- id: 123,
516
- email: 'user@example.com',
517
- renderingId: 456,
518
- serverToken: 'forest-server-token',
519
- exp: Math.floor(Date.now() / 1000) + 3600,
520
- iat: Math.floor(Date.now() / 1000),
521
- };
522
- jsonwebtoken_1.default.verify.mockReturnValue(mockDecoded);
523
- const provider = createProvider();
524
- const result = await provider.verifyAccessToken('valid-access-token');
525
- expect(result.token).toBe('valid-access-token');
526
- expect(result.clientId).toBe('123');
527
- expect(result.expiresAt).toBe(mockDecoded.exp);
528
- expect(result.scopes).toEqual(['mcp:read', 'mcp:write', 'mcp:action']);
529
- expect(result.extra).toEqual({
530
- userId: 123,
531
- email: 'user@example.com',
532
- renderingId: 456,
533
- environmentApiEndpoint: undefined,
534
- forestServerToken: 'forest-server-token',
535
- });
536
- });
537
- it('should throw error for expired access token', async () => {
538
- jsonwebtoken_1.default.verify.mockImplementation(() => {
539
- throw new jsonwebtoken_1.default.TokenExpiredError('jwt expired', new Date());
540
- });
541
- const provider = createProvider();
542
- await expect(provider.verifyAccessToken('expired-token')).rejects.toThrow('Access token has expired');
543
- });
544
- it('should throw error for invalid access token', async () => {
545
- jsonwebtoken_1.default.verify.mockImplementation(() => {
546
- throw new jsonwebtoken_1.default.JsonWebTokenError('invalid signature');
547
- });
548
- const provider = createProvider();
549
- await expect(provider.verifyAccessToken('invalid-token')).rejects.toThrow('Invalid access token');
550
- });
551
- it('should throw error when using refresh token as access token', async () => {
552
- jsonwebtoken_1.default.verify.mockReturnValue({
553
- type: 'refresh',
554
- clientId: 'test-client-id',
555
- });
556
- const provider = createProvider();
557
- await expect(provider.verifyAccessToken('refresh-token')).rejects.toThrow('Cannot use refresh token as access token');
558
- });
559
- it('should include environmentApiEndpoint after initialize is called', async () => {
560
- mockServer.get('/liana/environment', {
561
- data: {
562
- id: '12345',
563
- attributes: { api_endpoint: 'https://api.example.com' },
564
- },
565
- });
566
- global.fetch = mockServer.fetch;
567
- const mockDecoded = {
568
- id: 123,
569
- email: 'user@example.com',
570
- renderingId: 456,
571
- serverToken: 'forest-server-token',
572
- exp: Math.floor(Date.now() / 1000) + 3600,
573
- iat: Math.floor(Date.now() / 1000),
574
- };
575
- jsonwebtoken_1.default.verify.mockReturnValue(mockDecoded);
576
- const provider = createProvider();
577
- // Call initialize to fetch environment data
578
- await provider.initialize();
579
- const result = await provider.verifyAccessToken('valid-access-token');
580
- expect(result.extra).toEqual({
581
- userId: 123,
582
- email: 'user@example.com',
583
- renderingId: 456,
584
- environmentApiEndpoint: 'https://api.example.com',
585
- forestServerToken: 'forest-server-token',
586
- });
587
- });
588
- });
589
- });
590
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9yZXN0LW9hdXRoLXByb3ZpZGVyLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZm9yZXN0LW9hdXRoLXByb3ZpZGVyLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFHQSx5RkFBc0U7QUFDdEUsZ0VBQXdDO0FBRXhDLG9GQUEwRDtBQUMxRCwyRUFBa0Q7QUFFbEQsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztBQUMxQixJQUFJLENBQUMsSUFBSSxDQUFDLGlDQUFpQyxDQUFDLENBQUM7QUFFN0MsTUFBTSwyQkFBMkIsR0FBRyw0QkFFbkMsQ0FBQztBQUNGLE1BQU0sYUFBYSxHQUFHLHNCQUFZLENBQUMsTUFBbUIsQ0FBQztBQUN2RCxNQUFNLFdBQVcsR0FBRyxzQkFBWSxDQUFDLElBQWlCLENBQUM7QUFFbkQsTUFBTSxlQUFlLEdBQUcsaUJBQWlCLENBQUM7QUFDMUMsTUFBTSxnQkFBZ0IsR0FBRyxrQkFBa0IsQ0FBQztBQUM1QyxNQUFNLG1CQUFtQixHQUFHLDZCQUE2QixDQUFDO0FBRTFELFNBQVMsY0FBYyxDQUFDLGVBQWUsR0FBRyw2QkFBNkI7SUFDckUsT0FBTyxJQUFJLCtCQUFtQixDQUFDO1FBQzdCLGVBQWU7UUFDZixZQUFZLEVBQUUsbUJBQW1CO1FBQ2pDLFNBQVMsRUFBRSxlQUFlO1FBQzFCLFVBQVUsRUFBRSxnQkFBZ0I7UUFDNUIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxJQUFJO0tBQ3JCLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxRQUFRLENBQUMscUJBQXFCLEVBQUUsR0FBRyxFQUFFO0lBQ25DLElBQUksV0FBOEIsQ0FBQztJQUNuQyxJQUFJLFVBQXNCLENBQUM7SUFDM0IsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQztJQUVuQyxTQUFTLENBQUMsR0FBRyxFQUFFO1FBQ2IsV0FBVyxHQUFHLEVBQUUsR0FBRyxPQUFPLENBQUMsR0FBRyxFQUFFLENB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
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=filter.test.d.ts.map