Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
806cd2a
feat: Refactor authentication module structure and add new utilities
frontegg-david Jan 11, 2026
6de120e
feat: Enhance JWT payload handling and improve credential management
frontegg-david Jan 11, 2026
5dcf4e0
chore: Update peerDependencies and dependencies in package.json
frontegg-david Jan 11, 2026
b245b58
feat: Enhance consent handling and validation in authorization and to…
frontegg-david Jan 11, 2026
a93588c
Merge branch 'main' into split-auth-package
frontegg-david Jan 11, 2026
6aa5015
feat: Refactor consent handling and improve error handling in authori…
frontegg-david Jan 11, 2026
3e4fc73
Merge branch 'refs/heads/main' into split-auth-package
frontegg-david Jan 12, 2026
b3798f0
feat: Implement In-Memory Authorization Vault for development and tes…
frontegg-david Jan 12, 2026
2896832
feat: Update Jest configuration and project settings for improved bui…
frontegg-david Jan 12, 2026
9260c23
feat: Refactor storage and error handling, add TypedStorage and Encry…
frontegg-david Jan 12, 2026
e895928
Merge branch 'main' into split-auth-package
frontegg-david Jan 12, 2026
172607b
feat: Enhance encryption handling and refactor credential expiry extr…
frontegg-david Jan 12, 2026
c129a2b
feat: Implement base64 encoding/decoding functions and enhance creden…
frontegg-david Jan 12, 2026
b44d34d
feat: Refactor authentication module structure and re-export types fr…
frontegg-david Jan 12, 2026
0470ddc
feat: Add OAuth Authorization Store with in-memory and Redis implemen…
frontegg-david Jan 12, 2026
7a0f4ab
feat: Remove @frontmcp/plugin-config references and update dependencies
frontegg-david Jan 12, 2026
12db15e
feat: Remove @frontmcp/plugin-config references and update dependencies
frontegg-david Jan 12, 2026
848eb9c
feat: Refactor session management and update encryption methods
frontegg-david Jan 12, 2026
ab2b614
feat: Enhance session encryption handling and improve session creatio…
frontegg-david Jan 12, 2026
11b66b9
feat: Enhance session encryption handling and improve session creatio…
frontegg-david Jan 12, 2026
d651b3d
feat: Refactor authentication handling and enhance session management…
frontegg-david Jan 12, 2026
2b0e14f
feat: Refactor authentication handling and enhance session management…
frontegg-david Jan 13, 2026
3bc20d5
feat: Refactor authentication handling and enhance session management…
frontegg-david Jan 13, 2026
655e2ae
feat: Enhance error handling and validation in vault operations and i…
frontegg-david Jan 13, 2026
9359319
feat: Improve end-to-end tests for authentication and storage operati…
frontegg-david Jan 13, 2026
66332cd
feat: Improve end-to-end tests for authentication and storage operati…
frontegg-david Jan 13, 2026
a941cf6
feat: Enhance JWT verification by handling missing tokens and updatin…
frontegg-david Jan 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .claude/skills/fix-pr/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
name: fix-pr
description: Review a CodeRabbit PR comment and produce an action plan when prompted to analyze a review comment.
user-invocable: true
---

When asked to review a single CodeRabbit pull request comment, check whether it’s still valid, then plan fixes and test suggestions.
Fixes must include the nitpicks and outside diffs.

Expected input from the user:

- A single CodeRabbit review comment.

Output should include:

1. Validity (valid / no longer applies)
2. Clear reasoning
3. A fix plan with exact changes
4. Test commands to verify

Example:
User will provide:
1 change: 0 additions & 1 deletion apps/e2e/demo-e2e-agents/jest.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
testMatch: ['<rootDir>/e2e/**/*.e2e.test.ts'],
testTimeout: 60000,
forceExit: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/../../../libs/testing/src/setup.ts'],
transformIgnorePatterns: ['node_modules/(?!(jose)/)'],
Expand Down
1 change: 0 additions & 1 deletion apps/e2e/demo-e2e-cache/jest.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
testMatch: ['<rootDir>/e2e/**/*.e2e.test.ts'],
testTimeout: 60000,
forceExit: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/../../../libs/testing/src/setup.ts'],
transformIgnorePatterns: ['node_modules/(?!(jose)/)'],
Expand Down
1 change: 0 additions & 1 deletion apps/e2e/demo-e2e-codecall/jest.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
testMatch: ['<rootDir>/e2e/**/*.e2e.test.ts'],
testTimeout: 60000,
forceExit: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/../../../libs/testing/src/setup.ts'],
transformIgnorePatterns: ['node_modules/(?!(jose)/)'],
Expand Down
291 changes: 291 additions & 0 deletions apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
/**
* E2E Tests for Cross-Package Imports
*
* Tests that imports work correctly across the split packages:
* - @frontmcp/auth exports work correctly
* - @frontmcp/utils exports work correctly
* - @frontmcp/sdk re-exports from @frontmcp/auth
* - Type compatibility across packages
*
* These tests ensure the package split maintains a proper export structure.
*/
import { test, expect } from '@frontmcp/testing';

// Import from @frontmcp/auth directly
import {
JwksService,
InMemoryAuthorizationVault,
StorageAuthorizationVault,
TypedStorage,
VaultEncryption,
TinyTtlCache,
authUserSchema,
credentialTypeSchema,
} from '@frontmcp/auth';

// Import from @frontmcp/utils directly
import {
MemoryStorageAdapter,
hkdfSha256,
encryptAesGcm,
decryptAesGcm,
randomBytes,
sha256,
base64urlEncode,
base64urlDecode,
} from '@frontmcp/utils';

// Import SDK decorators and context types from @frontmcp/sdk
import { FrontMcp, Tool, ToolContext, Resource, ResourceContext, Prompt, PromptContext, App } from '@frontmcp/sdk';

test.describe('Cross-Package Imports E2E', () => {
test.use({
server: 'apps/e2e/demo-e2e-config/src/main.ts',
publicMode: true,
});

test.describe('@frontmcp/auth Exports', () => {
test('should export JwksService class', () => {
expect(JwksService).toBeDefined();
expect(typeof JwksService).toBe('function');
});

test('should export InMemoryAuthorizationVault class', () => {
expect(InMemoryAuthorizationVault).toBeDefined();
expect(typeof InMemoryAuthorizationVault).toBe('function');
});

test('should export StorageAuthorizationVault class', () => {
expect(StorageAuthorizationVault).toBeDefined();
expect(typeof StorageAuthorizationVault).toBe('function');
});

test('should export TypedStorage class', () => {
expect(TypedStorage).toBeDefined();
expect(typeof TypedStorage).toBe('function');
});

test('should export VaultEncryption class', () => {
expect(VaultEncryption).toBeDefined();
expect(typeof VaultEncryption).toBe('function');
});

test('should export TinyTtlCache class', () => {
expect(TinyTtlCache).toBeDefined();
expect(typeof TinyTtlCache).toBe('function');
});

test('should export Zod schemas', () => {
expect(authUserSchema).toBeDefined();
expect(credentialTypeSchema).toBeDefined();
});
});

test.describe('@frontmcp/utils Exports', () => {
test('should export MemoryStorageAdapter class', () => {
expect(MemoryStorageAdapter).toBeDefined();
expect(typeof MemoryStorageAdapter).toBe('function');
});

test('should export crypto functions', () => {
expect(hkdfSha256).toBeDefined();
expect(encryptAesGcm).toBeDefined();
expect(decryptAesGcm).toBeDefined();
expect(randomBytes).toBeDefined();
expect(sha256).toBeDefined();
});

test('should export base64url encoding functions', () => {
expect(base64urlEncode).toBeDefined();
expect(base64urlDecode).toBeDefined();
});
});

test.describe('@frontmcp/sdk Exports', () => {
test('should export FrontMcp decorator', () => {
expect(FrontMcp).toBeDefined();
});

test('should export Tool decorator and context', () => {
expect(Tool).toBeDefined();
expect(ToolContext).toBeDefined();
});

test('should export Resource decorator and context', () => {
expect(Resource).toBeDefined();
expect(ResourceContext).toBeDefined();
});

test('should export Prompt decorator and context', () => {
expect(Prompt).toBeDefined();
expect(PromptContext).toBeDefined();
});

test('should export App decorator', () => {
expect(App).toBeDefined();
});
});

test.describe('Instantiation Tests', () => {
test('should instantiate InMemoryAuthorizationVault', async () => {
const vault = new InMemoryAuthorizationVault();
expect(vault).toBeDefined();

// Should be able to create an entry
const entry = await vault.create({
userSub: 'test-user',
clientId: 'test-client',
});
expect(entry.id).toBeDefined();
expect(entry.userSub).toBe('test-user');
});

test('should instantiate MemoryStorageAdapter', () => {
const adapter = new MemoryStorageAdapter();
expect(adapter).toBeDefined();
});

test('should instantiate StorageAuthorizationVault with adapter', async () => {
const adapter = new MemoryStorageAdapter();
await adapter.connect(); // Must connect before use
const vault = new StorageAuthorizationVault(adapter);
expect(vault).toBeDefined();

// Should be able to create an entry
const entry = await vault.create({
userSub: 'storage-user',
clientId: 'storage-client',
});
expect(entry.id).toBeDefined();
});

test('should instantiate TypedStorage', async () => {
const adapter = new MemoryStorageAdapter();
await adapter.connect(); // Must connect before use
const storage = new TypedStorage<{ name: string }>(adapter);
expect(storage).toBeDefined();

// Should be able to set and get data
await storage.set('test-key', { name: 'test-value' });
const value = await storage.get('test-key');
expect(value).toEqual({ name: 'test-value' });
});

test('should instantiate TinyTtlCache', async () => {
const cache = new TinyTtlCache<string, string>(1000);
expect(cache).toBeDefined();

// Should be able to set and get values
cache.set('key1', 'value1');
expect(cache.get('key1')).toBe('value1');
});
});

test.describe('Crypto Function Tests', () => {
test('should perform HKDF key derivation', () => {
const encoder = new TextEncoder();
const ikm = encoder.encode('input-key-material');
const salt = encoder.encode('salt');
const info = encoder.encode('info');

const derivedKey = hkdfSha256(ikm, salt, info, 32);
expect(derivedKey).toBeDefined();
expect(derivedKey.length).toBe(32);
});

test('should encrypt and decrypt with AES-GCM', () => {
const key = randomBytes(32);
const iv = randomBytes(12); // AES-GCM requires 12-byte IV
const plaintext = 'Hello, World!';
const encoder = new TextEncoder();
const decoder = new TextDecoder();

// encryptAesGcm signature: (key, plaintext, iv)
const encrypted = encryptAesGcm(key, encoder.encode(plaintext), iv);
expect(encrypted).toBeDefined();
expect(encrypted.ciphertext).toBeDefined();
expect(encrypted.tag).toBeDefined();

// decryptAesGcm needs the same iv, ciphertext, and tag
const decrypted = decryptAesGcm(key, encrypted.ciphertext, iv, encrypted.tag);
expect(decoder.decode(decrypted)).toBe(plaintext);
});

test('should generate random bytes', () => {
const bytes16 = randomBytes(16);
const bytes32 = randomBytes(32);

expect(bytes16.length).toBe(16);
expect(bytes32.length).toBe(32);

// Should be different each time
const bytes16_2 = randomBytes(16);
expect(bytes16).not.toEqual(bytes16_2);
});

test('should compute SHA256 hash', () => {
const hash = sha256('test message');
expect(hash).toBeDefined();
expect(hash.length).toBe(32);
});

test('should encode/decode base64url', () => {
const original = new Uint8Array([1, 2, 3, 4, 5, 255, 254, 253]);
const encoded = base64urlEncode(original);
const decoded = base64urlDecode(encoded);

expect(encoded).toBeDefined();
expect(decoded).toEqual(original);
});
});

test.describe('Schema Validation Tests', () => {
test('should validate authUser schema', () => {
const validUser = {
sub: 'user-123',
email: 'user@example.com',
name: 'Test User',
};

const result = authUserSchema.safeParse(validUser);
expect(result.success).toBe(true);
});

test('should reject invalid authUser schema (missing required sub)', () => {
const invalidUser = { email: 'user@example.com' };
const invalidResult = authUserSchema.safeParse(invalidUser);
expect(invalidResult.success).toBe(false);
});

test('should validate credentialType schema', () => {
const validTypes = ['oauth', 'api_key', 'bearer', 'basic', 'private_key'];

for (const type of validTypes) {
const result = credentialTypeSchema.safeParse(type);
expect(result.success).toBe(true);
}

const invalidResult = credentialTypeSchema.safeParse('invalid_type');
expect(invalidResult.success).toBe(false);
});
});

test.describe('Integration with MCP Server', () => {
test('should connect and list tools', async ({ mcp }) => {
const tools = await mcp.tools.list();
expect(tools).toBeDefined();
// demo-e2e-config has several config-related tools
expect(tools).toContainTool('get-config');
});

test('should call a tool successfully', async ({ mcp }) => {
// Test actually calling a tool to verify full integration
const result = await mcp.tools.call('get-config', {
key: 'NODE_ENV',
defaultValue: 'test',
});
expect(result).toBeSuccessful();
expect(result).toHaveTextContent('NODE_ENV');
});
});
});
4 changes: 2 additions & 2 deletions apps/e2e/demo-e2e-config/jest.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
testMatch: ['<rootDir>/e2e/**/*.e2e.test.ts'],
testTimeout: 60000,
forceExit: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/../../../libs/testing/src/setup.ts'],
transformIgnorePatterns: ['node_modules/(?!(jose)/)'],
Expand All @@ -36,7 +35,8 @@ const config: Config.InitialOptions = {
'^@frontmcp/sdk$': '<rootDir>/../../../libs/sdk/src/index.ts',
'^@frontmcp/adapters$': '<rootDir>/../../../libs/adapters/src/index.ts',
'^@frontmcp/plugins$': '<rootDir>/../../../libs/plugins/src/index.ts',
'^@frontmcp/plugin-config$': '<rootDir>/../../../plugins/plugin-config/src/index.ts',
'^@frontmcp/auth$': '<rootDir>/../../../libs/auth/src/index.ts',
'^@frontmcp/utils$': '<rootDir>/../../../libs/utils/src/index.ts',
},
coverageDirectory: '../../../coverage/e2e/demo-e2e-config',
...e2eCoveragePreset,
Expand Down
1 change: 0 additions & 1 deletion apps/e2e/demo-e2e-direct/jest.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
testMatch: ['<rootDir>/e2e/**/*.e2e.test.ts'],
testTimeout: 60000,
forceExit: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/../../../libs/testing/src/setup.ts'],
transformIgnorePatterns: ['node_modules/(?!(jose)/)'],
Expand Down
1 change: 0 additions & 1 deletion apps/e2e/demo-e2e-errors/jest.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
testMatch: ['<rootDir>/e2e/**/*.e2e.test.ts'],
testTimeout: 60000,
forceExit: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/../../../libs/testing/src/setup.ts'],
transformIgnorePatterns: ['node_modules/(?!(jose)/)'],
Expand Down
1 change: 0 additions & 1 deletion apps/e2e/demo-e2e-hooks/jest.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
testMatch: ['<rootDir>/e2e/**/*.e2e.test.ts'],
testTimeout: 60000,
forceExit: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/../../../libs/testing/src/setup.ts'],
transformIgnorePatterns: ['node_modules/(?!(jose)/)'],
Expand Down
1 change: 0 additions & 1 deletion apps/e2e/demo-e2e-multiapp/jest.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
testMatch: ['<rootDir>/e2e/**/*.e2e.test.ts'],
testTimeout: 60000,
forceExit: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/../../../libs/testing/src/setup.ts'],
transformIgnorePatterns: ['node_modules/(?!(jose)/)'],
Expand Down
1 change: 0 additions & 1 deletion apps/e2e/demo-e2e-notifications/jest.e2e.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const config: Config.InitialOptions = {
testEnvironment: 'node',
testMatch: ['<rootDir>/e2e/**/*.e2e.test.ts'],
testTimeout: 60000,
forceExit: true,
maxWorkers: 1,
setupFilesAfterEnv: ['<rootDir>/../../../libs/testing/src/setup.ts'],
transformIgnorePatterns: ['node_modules/(?!(jose)/)'],
Expand Down
Loading
Loading