Skip to content

Conversation

@frontegg-david
Copy link
Contributor

@frontegg-david frontegg-david commented Jan 11, 2026

Summary by CodeRabbit

  • New Features

    • New publishable Auth package: centralized auth APIs, server-rendered OAuth UI, session/token encryption, credential vaults & provider system, key-persistence API, and filesystem-backed storage adapter.
  • Refactor

    • SDK now consumes unified auth/utils surfaces; tighter types, async-safe crypto flows, DI/provider wiring and credential caching.
  • Tests

    • Extensive new unit, integration, and E2E test suites across auth, storage, key-persistence, and vaults.
  • Docs

    • Added library README and refreshed auth documentation.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 11, 2026

📝 Walkthrough

Walkthrough

Adds a new publishable package @frontmcp/auth (JWKS, UI templates, session/token/vault, authorization types, AuthProviders vault and credential cache), a key-persistence and filesystem storage surface in @frontmcp/utils, integrates SDK to consume the new auth surface, and updates tests, e2e tooling, and configs.

Changes

Cohort / File(s) Summary
Package scaffold & config
libs/auth/README.md, libs/auth/package.json, libs/auth/project.json, libs/auth/tsconfig*.json, libs/auth/eslint.config.mjs, libs/auth/jest.config.cts, tsconfig.base.json, libs/sdk/tsconfig.lib.json
New NX library package @frontmcp/auth with build/test/publish targets, package exports, TS/Jest/ESLint configs, and path aliases.
Public barrels / exports
libs/auth/src/index.ts, libs/auth/src/*/index.ts, libs/utils/src/index.ts
New consolidated public API re-exports for JWKS, UI, session, vault, vault-encryption, token vault, utils and key-persistence primitives.
JWKS & persistence migration
libs/auth/src/jwks/jwks.service.ts, libs/auth/src/jwks/dev-key-persistence.ts, libs/auth/src/jwks/index.ts, libs/auth/src/jwks/*
JwksService now uses generic KeyPersistence (lazy) and standardized errors; legacy dev-key-persistence marked deprecated and tests updated/added.
UI templates & layout
libs/auth/src/ui/base-layout.ts, libs/auth/src/ui/templates.ts, libs/auth/src/ui/index.ts, libs/auth/src/ui/__tests__/*
Server-side Tailwind/CDN layouts and template builders (consent/login/error/etc.) with theme model and escaping utilities.
Session, Token Vault & vault encryption
libs/auth/src/session/token.vault.ts, libs/auth/src/session/vault-encryption.ts, libs/auth/src/session/token.store.ts, libs/auth/src/session/storage/*, libs/auth/src/session/utils/*
New TokenVault (A256GCM), async VaultEncryption using @frontmcp/utils primitives (hkdf/encryptAesGcm/decryptAesGcm), StorageTokenStore and storage-backed AuthorizationVault implementations; many APIs shifted to Uint8Array/async.
Authorization & vaults
libs/auth/src/session/authorization-vault.ts, libs/auth/src/session/storage/storage-authorization-vault.ts, libs/auth/src/session/storage/in-memory-authorization-vault.ts, libs/auth/src/authorization/*
New credential schemas, AuthorizationVault interface, InMemory and Storage implementations, and Zod schemas/types for auth state and progressive authorization.
Key persistence & crypto (utils)
libs/utils/src/crypto/key-persistence/*, libs/utils/src/crypto/hmac-signing.ts, libs/utils/src/crypto/index.ts, libs/utils/src/storage/adapters/filesystem.ts
New KeyPersistence types/schemas/class, factory auto-detecting storage, filesystem storage adapter (atomic writes/permissions), and HMAC signing utilities.
Storage adapters & tests
libs/utils/src/storage/adapters/filesystem.ts, libs/utils/src/storage/adapters/index.ts, libs/utils/src/storage/__tests__/*
FilesystemStorageAdapter added and many storage tests added/expanded; review atomic write, TTL semantics, key encoding.
SDK: AuthProviders & DI wiring
libs/sdk/src/auth/vault/*, libs/sdk/src/auth/vault/*.providers
New AuthProvidersRegistry, AuthProvidersVault, CredentialCache, eager/lazy loaders, AuthProvidersAccessorImpl, DI tokens/providers, and context extension for authProviders.
SDK session model refactor & removals
libs/sdk/src/auth/session/**
Consolidates session model to McpSession, removes legacy session/token/vault modules/tests, and rebinds many imports to @frontmcp/auth / @frontmcp/utils.
Credential cache & helpers
libs/auth/src/vault/credential-cache.ts, libs/auth/src/vault/credential-helpers.ts
New in-memory CredentialCache with TTL/eviction/stats and helper extractCredentialExpiry.
Tool metadata & tokens
libs/sdk/src/common/metadata/tool.metadata.ts, libs/sdk/src/common/tokens/tool.tokens.ts
Adds authProviders metadata field and corresponding token entry.
Housekeeping, docs & tests
apps/e2e/**/jest.e2e.config.ts, package.json, libs/plugins/*, libs/auth/docs/*, many test additions/removals`
Removed forceExit in many e2e Jest configs, added Jest moduleNameMapper aliases for auth/utils, bumped Prettier, removed plugin-config re-export, large additions of tests for auth/utils/storage and removals/rewires of legacy SDK tests.

Sequence Diagram(s)

sequenceDiagram
  participant SDK as SDK (consumer)
  participant Auth as `@frontmcp/auth` (JwksService)
  participant KP as `@frontmcp/utils` (KeyPersistence)
  participant FS as FileSystemStorageAdapter

  SDK->>Auth: request orchestrator signing key
  Auth->>Auth: shouldEnablePersistence()
  alt persistence enabled
    Auth->>KP: getAsymmetric("jwks-orchestrator")
    KP-->>Auth: AsymmetricKeyData | null
    alt persisted key found
      Auth-->>SDK: return persisted key
    else
      Auth->>Auth: generate new key
      Auth->>KP: set(asymmetricKeyData)
      KP->>FS: write key to disk
      FS-->>KP: ack
      KP-->>Auth: ack
      Auth-->>SDK: return new key
    end
  else persistence disabled
    Auth->>Auth: generate ephemeral key
    Auth-->>SDK: return ephemeral key
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐇 I hopped through branches, soft and spry,

Hid keys in burrows where secrets lie,
Vaults and templates stitched with Tailwind cheer,
Sessions snug in bytes, persistence near,
Exports bundled up — a tidy carrot, clear.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Refactor authentication module structure and add new utilities' clearly describes the main objective of the changeset, which involves extensive refactoring of the auth module structure (extracting libs/auth, creating new organizational patterns) and adding new utility modules (key persistence, HMAC signing, storage adapters, vault encryption). This is the primary focus of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 96.61% which is sufficient. The required threshold is 65.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🤖 Fix all issues with AI agents
In @libs/auth/jest.config.cts:
- Around line 13-20: The coverage thresholds in jest.config.cts are set too low
(statements: 65, branches: 60, functions: 40, lines: 65); update the
coverageThreshold global values for statements, branches, functions, and lines
to 95 (or 95+) to meet the coding guidelines, and if tests currently cannot meet
that target add a TODO or create a tracking issue referenced in the repo (e.g.,
update the coverageThreshold block and add an issue link) so the team can
incrementally raise test coverage to 95% across all metrics.

In @libs/auth/src/jwks/__tests__/jwks.utils.test.ts:
- Around line 100-106: The decodeJwtPayloadSafe implementation currently treats
any typeof === 'object' as valid, which lets arrays through; update
decodeJwtPayloadSafe to explicitly reject arrays by adding an Array.isArray(obj)
check and return undefined when true before casting to Record<string, unknown>,
ensuring the function respects the Record<string, unknown> return shape; then
update the test in jwks.utils.test.ts that constructs an array payload to expect
undefined instead of [1,2,3] (reference decodeJwtPayloadSafe and the test case
that builds arrayPayload).

In @libs/auth/src/session/authorization-vault.ts:
- Around line 34-42: The enum credentialTypeSchema currently omits three
credential types present in the discriminated union (ssh_key, service_account,
oauth_pkce), causing validation mismatches; update the z.enum([...]) in
credentialTypeSchema to include 'ssh_key', 'service_account', and 'oauth_pkce'
(with appropriate inline comments if desired) so it matches the variants used in
the credentialSchema discriminated union and avoid runtime validation errors.
- Around line 845-872: In refreshOAuthCredential, the early return currently
skips credentials whose credential.type !== 'oauth', so oauth_pkce entries
(which also carry accessToken/refreshToken/expiresAt) are ignored; change the
type check in refreshOAuthCredential (and any related comment) to accept both
'oauth' and 'oauth_pkce' (e.g., credential.credential.type === 'oauth' ||
credential.credential.type === 'oauth_pkce') or use a predicate like
startsWith('oauth') before returning, then proceed to update accessToken,
refreshToken, expiresAt, isValid, invalidReason, and entry.lastAccessAt as
currently implemented.

In @libs/auth/src/session/storage/storage-token-store.ts:
- Around line 55-63: The constructor currently creates a namespaced view when
given a NamespacedStorage but key() determines namespacing by inspecting
this.storage.raw, causing double-prefixing; change the logic to track whether
the original storage argument was namespaced (e.g., set a boolean like
storageIsNamespaced = this.isNamespacedStorage(storage) in the constructor) and
use that flag in key() instead of inspecting this.storage.raw so keys are only
namespaced once; update references to isNamespacedStorage, namespacedStorage,
this.storage and key() accordingly.

In @libs/auth/src/session/token.vault.ts:
- Around line 69-75: Replace the custom Buffer-based helpers by using the shared
utilities: remove or stop using the private methods toBase64Url and
fromBase64Url and instead import base64urlEncode and base64urlDecode from
@frontmcp/utils (add them to the existing import list) and call
base64urlEncode(...) where toBase64Url(...) was used and base64urlDecode(...)
where fromBase64Url(...) was used; ensure any references to the private methods
are updated and delete the now-unused private helper methods to avoid dead code.

In @libs/auth/src/session/vault-encryption.ts:
- Around line 188-189: The call to encryptAesGcm uses the wrong argument order;
change the invocation to match the signature encryptAesGcm(key: Uint8Array,
plaintext: Uint8Array, iv: Uint8Array) by passing key, new
TextEncoder().encode(plaintext), iv (i.e., swap the iv and plaintext args),
ensuring plaintext is a Uint8Array and iv remains a Uint8Array.
- Around line 227-229: The call to decryptAesGcm in vault-encryption.ts passes
arguments in the wrong order; update the invocation of decryptAesGcm(key,
ivBuffer, ciphertext, tagBuffer) to match the signature decryptAesGcm(key:
Uint8Array, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array) by swapping
the ivBuffer and ciphertext arguments so it becomes decryptAesGcm(key,
ciphertext, ivBuffer, tagBuffer); keep the same variable names and ensure types
remain Uint8Array.

In @libs/auth/src/ui/__tests__/templates.test.ts:
- Around line 1-606: Tests lack required coverage and missing XSS/edge-case/unit
tests; update jest.config.cts coverage thresholds to 95%+ and add focused tests:
add XSS assertions for buildConsentPage (clientName, app description, scope),
buildIncrementalAuthPage (app.description), buildFederatedLoginPage
(providerName/providerUrl/appIds), buildToolConsentPage (tool descriptions, app
fields, userName, userEmail), and buildLoginPage (scope); add tests for empty
arrays and missing optional fields (apps/providers/tools, iconUrl, description,
userName, userEmail, providerUrl), invalid/malformed inputs, and unit tests
exercising the internal buildAppCardHtml function and error/boundary paths so
branches/functions/lines all reach 95%+ coverage.

In @libs/auth/src/ui/base-layout.ts:
- Around line 165-173: The file defines a local escapeHtml function duplicate to
the shared implementation; remove the local export function escapeHtml and
instead import the canonical escapeHtml from @frontmcp/utils (use the same
symbol or align with how templates.ts uses baseEscapeHtml) so callers use the
single shared implementation; update any local references to escapeHtml to use
the imported symbol and remove the redundant code block to prevent divergence.

In @libs/sdk/src/auth/session/authorization-vault.ts:
- Around line 197-218: The pkceOAuthCredentialSchema currently includes a
persistent codeVerifier field which violates RFC 7636 and is unnecessary since
code verifiers are single-use and only needed by verifyPkce() during the initial
authorization code exchange; remove the codeVerifier property from
pkceOAuthCredentialSchema (delete the codeVerifier: z.string().optional() line
and its misleading comment), update any surrounding comment that claims it's
"stored for refresh", search for and remove any vault persistence or loading
logic that expects codeVerifier (e.g., in modules interacting with the vault or
token.refresh.ts) and ensure verifyPkce() continues to use the transient code
verifier passed at runtime rather than relying on stored credentials, then run
tests and update docs/comments accordingly.

In @libs/sdk/src/auth/vault/auth-providers.types.ts:
- Around line 188-201: The z.function() usages in credentialProviderConfigSchema
(fields factory, refresh, toHeaders) are incompatible with Zod 4; update
credentialProviderConfigSchema to define each function with explicit
input/output schemas matching the TS function types—e.g., make factory and
refresh accept the CredentialFactoryContext type (use an appropriate
z.instanceof/Object surrogate or schema) and return a Promise of unknown|null,
and make toHeaders accept an unknown and return z.record(z.string());
alternatively, if strict validation isn't needed, mark those fields as
z.unknown()/.passthrough() or remove them from strict validation and validate
functions separately.

In @libs/sdk/src/auth/vault/auth-providers.vault.ts:
- Around line 249-265: In buildVaultKey, avoid silently falling back to session
when scope === 'user' and userId is missing; instead validate and throw a clear
error (e.g., Error or a specific VaultAuthError) when userId is undefined so
callers must provide a userId for 'user' scope; update any callers of
buildVaultKey (or the public API that accepts scope/userId) to handle/propagate
this error and adjust tests to expect the new validation behavior.

In @libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts:
- Around line 139-149: The extractExpiry method is duplicated in eager-loader.ts
and lazy-loader.ts; create a shared helper: add a new credential-helpers.ts
exporting a function extractCredentialExpiry(credential: Credential): number |
undefined (same switch logic), update eager-loader.ts and lazy-loader.ts to
import and call extractCredentialExpiry instead of their local extractExpiry
implementations, remove the duplicate extractExpiry methods, and add an export
for extractCredentialExpiry in the credential-loaders index.ts barrel.
🟡 Minor comments (13)
libs/auth/src/session/storage/__tests__/storage-token-store.test.ts-170-181 (1)

170-181: Weak test assertion - no meaningful verification.

This test stores an expired blob but has no assertion verifying the expected behavior. The comment "May or may not exist depending on timing" indicates the test is non-deterministic. Either assert that the record exists immediately after put (before the 1s minimum TTL expires), or remove this test if the behavior is intentionally undefined.

Suggested fix - add deterministic assertion
     it('should use minimum TTL for expired blobs', async () => {
       const id = tokenStore.allocId();
       const pastExp = Math.floor(Date.now() / 1000) - 100; // Expired 100 seconds ago
       const blob = createTestBlob(pastExp);

       await tokenStore.put(id, blob);

-      // The record should still be stored (TTL set to 1 second minimum)
-      // but will expire very soon
+      // The record should still be stored immediately after put
+      // (TTL is set to minimum 1 second for expired blobs)
       const record = await tokenStore.get(id);
-      // May or may not exist depending on timing
+      expect(record).toBeDefined();
+      expect(record?.blob.exp).toBe(pastExp);
     });
libs/utils/src/crypto/key-persistence/key-persistence.ts-160-165 (1)

160-165: Cache is cleared even if storage deletion fails.

The delete method removes from cache before calling storage.delete, but if the storage operation fails, the cache is already cleared, leaving an inconsistent state.

🐛 Proposed fix
   async delete(kid: string): Promise<boolean> {
-    // Remove from cache
-    this.cache.delete(kid);
-
-    return this.storage.delete(kid);
+    const deleted = await this.storage.delete(kid);
+    // Only clear cache after successful deletion
+    this.cache.delete(kid);
+    return deleted;
   }
libs/auth/src/session/storage/storage-token-store.ts-118-126 (1)

118-126: TTL calculation returns 1 for already-expired tokens.

When exp is in the past (ttl <= 0), the code returns 1 second instead of undefined. This stores expired tokens briefly rather than rejecting them or letting them expire immediately.

Consider whether storing already-expired tokens is intended behavior, or if put() should reject them with an error or return early.

libs/auth/src/ui/base-layout.ts-293-295 (1)

293-295: Raw content injection creates XSS risk if caller passes unsanitized user input.

The content parameter is inserted directly into the HTML without escaping. While the caller is expected to provide safe HTML, this creates an implicit contract that could be violated.

Consider documenting this clearly or providing a safe variant:

/**
 * @param content - Pre-sanitized HTML content. Caller is responsible for escaping user input.
 */

Alternatively, provide a text-only variant that auto-escapes content for simple use cases.

package.json-87-87 (1)

87-87: Prettier v3 has breaking changes—verify formatting after upgrade, particularly for text wrapping and CJK spacing.

Prettier v3 introduced breaking changes compared to v2, including modified trailing comma behavior, Markdown/CJK spacing rules, and text unwrapping logic. Your .prettierrc already sets "trailingComma": "all" (Prettier v3's default) and "proseWrap": "preserve", which mitigates some breaking changes. However, other formatting differences may still occur, especially in Markdown or edge cases where text wrapping behavior changed. Since lint-staged (line 25) runs prettier on all staged files, test the formatting across the codebase to ensure the output matches expectations before merging.

libs/auth/src/jwks/__tests__/jwks.utils.test.ts-81-84 (1)

81-84: Test may not validate intended behavior.

The test on line 83 expects 'header.no-signature' to return undefined for having "less than 2 parts," but this string contains a dot and would be split into 2 parts: ['header', 'no-signature']. This would pass the length check parts.length < 2 and proceed to attempt decoding, likely failing later due to invalid base64. Consider using a token without any dots (e.g., 'only-one-part' alone) or one with exactly 1 dot to properly test this condition, or update the test expectation.

🧪 Proposed fix for the test
-  it('should return undefined for token with less than 2 parts', () => {
-    expect(decodeJwtPayloadSafe('only-one-part')).toBeUndefined();
-    expect(decodeJwtPayloadSafe('header.no-signature')).toBeUndefined();
-  });
+  it('should return undefined for token with less than 2 parts', () => {
+    expect(decodeJwtPayloadSafe('only-one-part')).toBeUndefined();
+  });
+
+  it('should return undefined for token with invalid payload decoding', () => {
+    expect(decodeJwtPayloadSafe('header.invalid-payload')).toBeUndefined();
+  });
libs/auth/src/jwks/jwks.service.ts-75-83 (1)

75-83: Potential race condition in lazy initialization.

The getKeyPersistence method has a race condition:

if (!this.keyPersistence) {
  this.keyPersistence = await createKeyPersistence({...});
}

If multiple concurrent calls occur before the first createKeyPersistence completes, multiple instances could be created. While this may not cause data corruption (KeyPersistence uses the same underlying storage), it could lead to:

  • Wasted resources creating multiple instances
  • Cache inconsistencies between instances
🔒 Proposed fix using promise guard pattern

Similar to the keyInitPromise pattern used in ensureOrchestratorKey:

+  private keyPersistencePromise?: Promise<KeyPersistence>;
+
   private async getKeyPersistence(): Promise<KeyPersistence | null> {
     if (!this.shouldEnablePersistence()) return null;
-    if (!this.keyPersistence) {
+    
+    if (this.keyPersistence) return this.keyPersistence;
+    
+    if (!this.keyPersistencePromise) {
-      this.keyPersistence = await createKeyPersistence({
+      this.keyPersistencePromise = createKeyPersistence({
         baseDir: '.frontmcp/keys',
       });
     }
+    
+    this.keyPersistence = await this.keyPersistencePromise;
     return this.keyPersistence;
   }
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts-33-53 (1)

33-53: Potential type safety issue with concurrent loads of different credential types.

The deduplication logic casts the in-flight promise to match the caller's generic type T, but this could lead to type mismatches if two concurrent callers request the same provider with different credential type expectations.

For example:

// Thread 1: load<OAuthCredential>('github', context)
// Thread 2: load<ApiKeyCredential>('github', context)  
// Thread 2 gets Thread 1's promise but expects wrong type

This scenario is unlikely in practice (providers typically produce one credential type), but the type system doesn't enforce this constraint.

💡 Consider one of these approaches

Option 1: Document the assumption

 /**
  * Load a credential lazily.
  * If already loading, returns the in-flight promise (deduplication).
+ * 
+ * Note: Assumes each provider name maps to a single credential type.
+ * Concurrent loads with different type parameters will share the same promise.
  *
  * @param config - Provider configuration
  * @param context - Factory context
  * @returns Resolved credential or null
  */

Option 2: Add type checking (runtime overhead)

// Store provider name -> credential type mapping
private readonly typeMap = new Map<string, string>();

async load<T extends Credential>(config: NormalizedProviderConfig<T>, context: CredentialFactoryContext) {
  const existing = this.loading.get(config.name);
  if (existing) {
    const expectedType = this.typeMap.get(config.name);
    if (expectedType && expectedType !== config.factory.name) {
      throw new Error(`Type mismatch for provider ${config.name}`);
    }
    // ...
  }
  this.typeMap.set(config.name, config.factory.name);
  // ...
}
libs/auth/src/ui/__tests__/templates.test.ts-24-147 (1)

24-147: Add XSS tests for all user-provided fields.

While XSS testing is present for clientName (lines 100-108), similar tests are missing for other user-provided fields that are rendered in the HTML:

  • appName (lines 53-57)
  • description (lines 59-63)
  • requiredScopes (lines 65-70)
  • iconUrl (lines 110-125)

Each field that renders user content should have explicit XSS injection tests.

Based on learnings about comprehensive XSS testing for all user-provided content in templates.

libs/auth/src/ui/__tests__/templates.test.ts-321-441 (1)

321-441: Add XSS injection tests.

This suite is missing XSS escaping tests for user-provided fields: toolName, description, appName, userName, and userEmail. All user-provided content rendered in HTML must be tested against injection attacks.

Based on learnings about XSS testing requirements.

libs/auth/src/ui/__tests__/templates.test.ts-214-319 (1)

214-319: Add XSS escaping tests for user-provided fields.

Missing XSS injection tests for fields like providerName, providerUrl, and clientName that are rendered in the HTML output. These should be tested with malicious payloads to ensure proper escaping.

Based on learnings about comprehensive XSS testing for templates.

libs/auth/src/ui/templates.ts-254-342 (1)

254-342: Unused csrfToken parameter in buildFederatedLoginPage.

The csrfToken is accepted as a parameter but never rendered in the form. This form uses method="GET" which doesn't require CSRF protection, so either:

  1. Remove the unused parameter for clarity, or
  2. If this should be a POST form for security, add the CSRF token field
🔧 Option 1: Remove unused parameter
 export function buildFederatedLoginPage(params: {
   providers: ProviderCard[];
   clientName: string;
   pendingAuthId: string;
-  csrfToken: string;
   callbackPath: string;
 }): string {
-  const { providers, clientName, pendingAuthId, csrfToken, callbackPath } = params;
+  const { providers, clientName, pendingAuthId, callbackPath } = params;
libs/auth/src/session/storage/storage-authorization-vault.ts-121-131 (1)

121-131: Silent failure on update() when entry doesn't exist.

The method silently returns without indication when the entry is not found. Callers cannot distinguish between "update succeeded" and "entry didn't exist." Consider throwing an error or returning a boolean indicating success.

🔧 Suggested improvement
-  async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<void> {
+  async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<boolean> {
     const entry = await this.get(id);
-    if (!entry) return;
+    if (!entry) return false;
 
     const updated = {
       ...entry,
       ...updates,
       lastAccessAt: Date.now(),
     };
     await this.storage.set(this.key(id), updated);
+    return true;
   }
🧹 Nitpick comments (47)
libs/auth/src/session/utils/tiny-ttl-cache.ts (1)

34-36: size() includes expired entries that haven't been cleaned up.

The size() method returns map.size, which includes expired entries that haven't been accessed or explicitly cleaned. Consider either documenting this behavior or calling cleanup() internally for accurate counts.

Option 1: Document the behavior
+  /**
+   * Returns the number of entries (including expired but not-yet-cleaned entries).
+   * Call cleanup() first for an accurate count of live entries.
+   */
   size(): number {
     return this.map.size;
   }
Option 2: Return accurate count
+  /**
+   * Returns the number of live (non-expired) entries.
+   */
   size(): number {
+    this.cleanup();
     return this.map.size;
   }
libs/utils/src/storage/adapters/__tests__/filesystem.test.ts (1)

246-249: Consider asserting specific error type or message.

The test asserts that incrementing a non-integer throws with message containing "not an integer", but doesn't verify the error type. Consider using toThrow(SomeSpecificError) if there's a dedicated error class.

libs/auth/src/session/vault-encryption.ts (2)

106-110: Consider documenting that pepper should come from environment variables.

Per learnings, encryption keys should not be hardcoded. While the config allows passing a pepper, the example at Line 89 shows process.env.VAULT_PEPPER, which is good. Consider adding a note in the JSDoc that the pepper should be sourced from environment variables for production use.


254-257: Unsafe type assertion in decryptObject.

JSON.parse(plaintext) as T performs an unsafe cast without validation. If the decrypted JSON doesn't match the expected type T, callers will get runtime errors.

Consider accepting an optional Zod schema for validation:

♻️ Suggested improvement
-  async decryptObject<T>(encrypted: EncryptedData, key: Uint8Array): Promise<T> {
+  async decryptObject<T>(encrypted: EncryptedData, key: Uint8Array, schema?: z.ZodType<T>): Promise<T> {
     const plaintext = await this.decrypt(encrypted, key);
-    return JSON.parse(plaintext) as T;
+    const parsed = JSON.parse(plaintext);
+    if (schema) {
+      return schema.parse(parsed);
+    }
+    return parsed as T;
   }
libs/auth/src/jwks/dev-key-persistence.ts (1)

22-26: Direct use of node:crypto in deprecated module.

Line 23 imports crypto directly, and Line 254 uses crypto.randomBytes. While this violates the coding guideline to use @frontmcp/utils for cryptographic operations, since this module is deprecated and scheduled for removal, the effort to fix may not be justified.

Consider adding a comment noting this deviation or prioritizing removal of this module. Based on learnings, prefer @frontmcp/utils for cryptographic operations.

libs/auth/src/ui/base-layout.ts (1)

35-44: Consider using theme.cdn configuration instead of hardcoded CDN URLs.

Based on learnings, the libs/uipack guidelines specify not to hard-code CDN URLs and instead use theme.cdn configuration. This would allow consumers to override CDN endpoints for different environments or self-hosted scenarios.

💡 Suggested approach
+export interface CdnConfig {
+  tailwind?: string;
+  fonts?: {
+    preconnect?: string[];
+    stylesheet?: string;
+  };
+}

 export const CDN = {
   /** Tailwind CSS v4 Browser CDN - generates styles on-the-fly with @theme support */
   tailwind: 'https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4',

   /** Google Fonts - Inter for modern UI */
   fonts: {
     preconnect: ['https://fonts.googleapis.com', 'https://fonts.gstatic.com'],
     stylesheet: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap',
   },
 } as const;

Then accept an optional cdn override in BaseLayoutOptions to merge with defaults.

libs/auth/src/session/authorization-vault.ts (3)

1071-1074: Redis cleanup() is a placeholder - add implementation or TODO tracking.

The in-memory implementation properly cleans up expired pending auths, but the Redis version is empty. For production use, this should implement SCAN-based cleanup or document that external cleanup (e.g., Redis TTL) is expected.

💡 Suggested approach
async cleanup(): Promise<void> {
  // TODO: Implement Redis cleanup using SCAN
  // Consider:
  // 1. Use SCAN to iterate vault keys
  // 2. Parse each entry and filter expired pendingAuths
  // 3. Update entries with cleaned pendingAuths array
  // Alternatively, document that Redis TTL should be used for automatic expiration
}

Do you want me to generate a full Redis SCAN-based cleanup implementation?


884-889: any type for Redis client - consider a minimal interface.

While the eslint-disable comment acknowledges this, defining a minimal interface would improve type safety without requiring a specific Redis client library.

💡 Suggested interface
interface RedisClient {
  get(key: string): Promise<string | null>;
  set(key: string, value: string): Promise<unknown>;
  del(key: string): Promise<number>;
}

export class RedisAuthorizationVault implements AuthorizationVault {
  constructor(
    private readonly redis: RedisClient,
    private readonly namespace = 'vault:',
  ) {}

927-929: No TTL set on Redis keys - vault entries could accumulate indefinitely.

Unlike the in-memory implementation which can be garbage collected, Redis entries persist. Consider adding TTL support based on session lifetime or implementing periodic cleanup.

Also applies to: 1000-1001

libs/sdk/src/auth/flows/session.verify.flow.ts (2)

188-194: Refactor nested ternary for readability.

The deeply nested ternary operator makes the sessionProtocol assignment logic difficult to follow. Consider using if-else statements or extracting to a helper function.

♻️ Proposed refactor using if-else statements
-    const sessionProtocol = httpTransportHeader
-      ? 'http'
-      : sessionIdRawHeader
-        ? 'streamable-http'
-        : sessionIdQuery
-          ? 'sse'
-          : undefined;
+    let sessionProtocol: string | undefined;
+    if (httpTransportHeader) {
+      sessionProtocol = 'http';
+    } else if (sessionIdRawHeader) {
+      sessionProtocol = 'streamable-http';
+    } else if (sessionIdQuery) {
+      sessionProtocol = 'sse';
+    } else {
+      sessionProtocol = undefined;
+    }

302-302: Address the TODO comment.

The TODO indicates that provider handling needs to be fixed. This should be resolved before merging to ensure the JwksService is properly integrated with the dependency injection system.

Do you want me to help generate a solution for proper provider integration, or should this be tracked in a separate issue?

libs/auth/tsconfig.spec.json (1)

6-6: Use modern module resolution instead of deprecated "node10".

The "node10" module resolution strategy is deprecated. Use "node" for backward compatibility or "node16"/"nodenext" for modern Node.js ESM/CJS resolution.

♻️ Recommended fix
-    "moduleResolution": "node10",
+    "moduleResolution": "node",
libs/auth/package.json (1)

47-59: Consider adding zod to devDependencies.

While zod is correctly declared as a peer dependency, it should also be included in devDependencies to support local development and testing. Without it, developers working on this package may encounter import errors when running tests or building the package.

📦 Add zod to devDependencies
  "devDependencies": {
    "@vercel/kv": "^3.0.0",
    "ioredis": "^5.8.0",
-   "typescript": "^5.9.3"
+   "typescript": "^5.9.3",
+   "zod": "^4.0.0"
  }
libs/utils/src/crypto/key-persistence/factory.ts (1)

97-106: Consider validating storage adapter connection state.

The documentation states "The storage adapter must already be connected" (line 82), but the function doesn't verify this assumption. If a caller passes a disconnected adapter, it may lead to runtime errors when KeyPersistence attempts operations.

Consider either:

  1. Adding a connection state check (if StorageAdapter exposes this)
  2. Calling await storage.connect() to ensure connectivity
  3. Strengthening the JSDoc to emphasize this is a strict precondition
🛡️ Proposed validation check

If StorageAdapter has an isConnected() method:

 export function createKeyPersistenceWithStorage(
   storage: StorageAdapter,
   options?: Pick<CreateKeyPersistenceOptions, 'throwOnInvalid' | 'enableCache'>,
 ): KeyPersistence {
+  // Verify storage is connected
+  if (typeof storage.isConnected === 'function' && !storage.isConnected()) {
+    throw new Error('Storage adapter must be connected before creating KeyPersistence');
+  }
+
   return new KeyPersistence({
     storage,
     throwOnInvalid: options?.throwOnInvalid ?? false,
     enableCache: options?.enableCache ?? true,
   });
 }
libs/auth/src/ui/__tests__/base-layout.test.ts (1)

349-361: Consider expanding authLayout test coverage.

While the current tests verify that authLayout is a function and generates valid HTML, consider adding tests for:

  • How it handles different bodyClass configurations (since it's a createLayout with a default bodyClass)
  • Theme merging behavior specific to authLayout
  • Interaction with other options (headExtra, description, etc.)

This would ensure parity with the coverage depth of other layout functions.

libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (2)

91-98: Improve error logging to include provider name on rejection.

When a promise rejects, the provider name is lost because it's only captured in the fulfilled result. Consider restructuring to preserve the provider name in error cases.

♻️ Suggested improvement
     const promises = providerNames.map(async (name) => {
-      const result = await this.get(name, options);
-      return { name, result };
+      try {
+        const result = await this.get(name, options);
+        return { name, result, error: null };
+      } catch (error) {
+        return { name, result: null, error };
+      }
     });

-    const settled = await Promise.allSettled(promises);
+    const settled = await Promise.all(promises);

-    for (const outcome of settled) {
-      if (outcome.status === 'fulfilled') {
-        results.set(outcome.value.name, outcome.value.result);
-      } else {
-        // Find the provider name from the error context if possible
-        this.logger?.warn('Failed to load credential:', outcome.reason);
-      }
+    for (const outcome of settled) {
+      if (outcome.error) {
+        this.logger?.warn(`Failed to load credential "${outcome.name}":`, outcome.error);
+      }
+      results.set(outcome.name, outcome.result);
     }

117-126: Consider parallel header retrieval for better performance.

The current implementation processes providers sequentially. Since getMany already supports parallel loading, headersMany could benefit from the same approach.

♻️ Parallel implementation
   async headersMany(providerNames: string[]): Promise<Record<string, string>> {
-    const allHeaders: Record<string, string> = {};
-
-    for (const name of providerNames) {
-      const headers = await this.headers(name);
-      Object.assign(allHeaders, headers);
-    }
-
-    return allHeaders;
+    const headerPromises = providerNames.map((name) => this.headers(name));
+    const headerResults = await Promise.all(headerPromises);
+    return Object.assign({}, ...headerResults);
   }
libs/auth/src/session/storage/__tests__/typed-storage.test.ts (1)

129-156: Consider adding test for mset with per-entry TTL options.

The TypedSetEntry<T> type likely supports per-entry options including TTL. Adding a test case that exercises mset with different TTL values per entry would improve coverage.

💚 Suggested test addition
it('should support TTL option per entry in mset', async () => {
  const users: TestUser[] = [
    { id: '1', name: 'Alice' },
    { id: '2', name: 'Bob' },
  ];

  await storage.mset([
    { key: 'user:1', value: users[0], options: { ttlSeconds: 3600 } },
    { key: 'user:2', value: users[1], options: { ttlSeconds: 7200 } },
  ]);

  const ttl1 = await storage.ttl('user:1');
  const ttl2 = await storage.ttl('user:2');
  
  expect(ttl1).toBeLessThanOrEqual(3600);
  expect(ttl1).toBeGreaterThan(0);
  expect(ttl2).toBeLessThanOrEqual(7200);
  expect(ttl2).toBeGreaterThan(3600); // Should be higher than ttl1
});
libs/sdk/src/auth/vault/auth-providers.context-extension.ts (1)

60-66: Consider extracting the error message to avoid duplication.

The error message is duplicated between authProvidersContextExtension.errorMessage and getAuthProviders. Extracting to a shared constant would improve maintainability.

♻️ DRY suggestion
+const AUTH_PROVIDERS_NOT_CONFIGURED_MESSAGE =
+  'AuthProviders vault is not configured. ' +
+  'Ensure AuthProvidersVault is enabled in your FrontMcp configuration with registered providers.';
+
 export const authProvidersContextExtension: ContextExtension = {
   property: 'authProviders',
   token: AUTH_PROVIDERS_ACCESSOR,
-  errorMessage:
-    'AuthProviders vault is not configured. ' +
-    'Ensure AuthProvidersVault is enabled in your FrontMcp configuration with registered providers.',
+  errorMessage: AUTH_PROVIDERS_NOT_CONFIGURED_MESSAGE,
 };

 export function getAuthProviders(ctx: { get: <T>(token: unknown) => T }): AuthProvidersAccessor {
   try {
     return ctx.get(AUTH_PROVIDERS_ACCESSOR);
   } catch (error) {
-    throw new Error(
-      'AuthProviders vault is not configured. ' +
-        'Ensure AuthProvidersVault is enabled in your FrontMcp configuration with registered providers.',
-      { cause: error },
-    );
+    throw new Error(AUTH_PROVIDERS_NOT_CONFIGURED_MESSAGE, { cause: error });
   }
 }

Also applies to: 80-90

libs/sdk/src/auth/vault/auth-providers.vault.ts (3)

98-98: Unsafe type assertion without runtime validation.

The as T cast at line 98 bypasses TypeScript's type checking without validating that the credential actually matches type T. If the stored credential doesn't match the expected type, this could cause runtime errors in calling code.

Consider adding runtime validation or returning the credential with its actual type:

Proposed fix
-      return appCredential.credential as T;
+      // Return the credential - caller should validate if specific type is needed
+      return appCredential.credential as T;
+      // Alternative: Add schema validation here if T's shape is known

121-127: Inconsistent error handling across credential operations.

removeCredential and invalidateCredential catch and log errors but don't rethrow them, while storeCredential (line 57-60) and refreshOAuthCredential (line 176-179) do rethrow. This inconsistency could make debugging difficult and lead to silent failures.

Consider either:

  1. Rethrowing errors consistently across all methods
  2. Documenting why some operations intentionally swallow errors
Proposed fix for consistent error handling
   async removeCredential(
     sessionId: string,
     providerId: string,
     scope: CredentialScope,
     userId?: string,
   ): Promise<void> {
     const vaultKey = this.buildVaultKey(sessionId, scope, userId);

     try {
       await this.baseVault.removeAppCredential(vaultKey, this.namespace, providerId);
       this.logger?.debug(`Removed credential "${providerId}"`);
     } catch (error) {
       this.logger?.warn(`Failed to remove credential "${providerId}":`, error);
+      throw error;
     }
   }

Also applies to: 144-153


195-196: Empty catch blocks silently swallow errors.

Multiple empty catch blocks in getAllCredentials silently discard errors with just // Ignore comments. While aggregating credentials across scopes may warrant continuing on partial failures, consider logging these errors at debug level to aid troubleshooting.

Proposed improvement
     } catch {
-      // Ignore
+      // Continue on partial failures - log for debugging
+      this.logger?.debug('Failed to get session scope credentials');
     }

Also applies to: 210-212, 222-224, 234-236

libs/auth/src/session/storage/__tests__/storage-authorization-vault.test.ts (1)

702-724: Potential flaky test due to timing sensitivity.

The cleanup test uses a 1ms TTL and waits only 10ms before asserting. While this works most of the time, it could be flaky in slow CI environments or under high load. Consider using a more robust approach.

Suggested improvement
-      // Create a pending auth with very short TTL
-      await vault.createPendingAuth(entry.id, {
-        appId: 'app1',
-        authUrl: 'https://auth.example.com/authorize',
-        ttlMs: 1, // 1ms TTL
-      });
-
-      // Wait for it to expire
-      await new Promise((resolve) => setTimeout(resolve, 10));
+      // Create a pending auth with short TTL
+      await vault.createPendingAuth(entry.id, {
+        appId: 'app1',
+        authUrl: 'https://auth.example.com/authorize',
+        ttlMs: 50, // 50ms TTL - short but not too aggressive
+      });
+
+      // Wait for it to expire with buffer
+      await new Promise((resolve) => setTimeout(resolve, 100));
libs/sdk/src/auth/vault/auth-providers.providers.ts (1)

103-112: Consider safer access to optional auth claims.

The code accesses ctx.authInfo?.extra?.['sub'] etc. with type assertions to string | undefined. If extra contains non-string values for these fields, this could cause issues downstream.

Consider adding runtime type guards:

Proposed improvement
       const factoryContext: CredentialFactoryContext = {
         sessionId: ctx.sessionId,
-        userSub: ctx.authInfo?.extra?.['sub'] as string | undefined,
-        userEmail: ctx.authInfo?.extra?.['email'] as string | undefined,
-        userName: ctx.authInfo?.extra?.['name'] as string | undefined,
+        userSub: typeof ctx.authInfo?.extra?.['sub'] === 'string' ? ctx.authInfo.extra['sub'] : undefined,
+        userEmail: typeof ctx.authInfo?.extra?.['email'] === 'string' ? ctx.authInfo.extra['email'] : undefined,
+        userName: typeof ctx.authInfo?.extra?.['name'] === 'string' ? ctx.authInfo.extra['name'] : undefined,
libs/auth/src/session/token.store.ts (3)

44-50: Consider using a proper Redis client type instead of any.

The comment suggests replacing any with "your redis client type", but for a published SDK library, this isn't ideal. Consider:

  1. Using a generic type parameter with a minimal interface
  2. Defining a minimal RedisClient interface that covers the needed methods

As per coding guidelines, avoid any types without strong justification.

Proposed improvement
+/** Minimal Redis client interface for TokenStore */
+export interface RedisClientLike {
+  set(key: string, value: string, options?: { EX?: number }): Promise<unknown>;
+  get(key: string): Promise<string | null>;
+  del(key: string): Promise<number>;
+}
+
 /** Redis token store - replace `any` with your redis client type. */
 export class RedisTokenStore implements TokenStore {
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
   constructor(
-    private readonly redis: any,
+    private readonly redis: RedisClientLike,
     private readonly ns = 'tok:',
   ) {}

58-62: Consider implementing Redis TTL based on blob expiry.

The comment mentions setting EX by blob.exp for Redis eviction, but this isn't implemented. If tokens have expiry times, leveraging Redis TTL would automatically clean up expired tokens.

Proposed implementation
   async put(id: string, blob: EncBlob) {
     const rec = JSON.stringify({ id, blob, updatedAt: Date.now() });
-    // Optional: set EX by blob.exp if you want Redis eviction at token expiry
-    await this.redis.set(this.key(id), rec);
+    if (blob.exp) {
+      const ttlSeconds = Math.max(0, blob.exp - Math.floor(Date.now() / 1000));
+      if (ttlSeconds > 0) {
+        await this.redis.set(this.key(id), rec, { EX: ttlSeconds });
+        return;
+      }
+    }
+    await this.redis.set(this.key(id), rec);
   }

64-72: Add error handling for Redis operations.

Redis operations can fail due to network issues, connection problems, etc. Consider adding try-catch blocks or letting errors propagate with meaningful context.

Proposed improvement
   async get(id: string) {
-    const raw = await this.redis.get(this.key(id));
-    if (!raw) return undefined;
-    return JSON.parse(raw) as SecretRecord;
+    const raw = await this.redis.get(this.key(id));
+    if (!raw) return undefined;
+    try {
+      return JSON.parse(raw) as SecretRecord;
+    } catch {
+      // Corrupted data - treat as missing
+      return undefined;
+    }
   }
libs/auth/src/session/token.vault.ts (2)

37-40: Add parameter validation to rotateTo method.

The rotateTo method doesn't validate its input parameter. It should verify that the key parameter is defined and has valid kid and key properties before updating internal state.

♻️ Proposed fix
 rotateTo(k: VaultKey) {
+  if (!k || !k.kid || !k.key) {
+    throw new Error('Invalid VaultKey: kid and key are required');
+  }
   this.active = k;
   this.keys.set(k.kid, k.key);
 }

57-67: Consider checking expiration before decrypting.

The decrypt method decrypts the blob before checking if it has expired. For better performance and user experience, consider validating blob.exp (if present) before performing the decryption operation.

♻️ Proposed enhancement
 async decrypt(blob: EncBlob): Promise<string> {
+  // Check expiration first to avoid unnecessary decryption
+  if (blob.exp && Date.now() > blob.exp * 1000) {
+    throw new Error(`vault_expired:${blob.kid}`);
+  }
+
   const key = this.keys.get(blob.kid);
   if (!key) throw new Error(`vault_unknown_kid:${blob.kid}`);

   const iv = this.fromBase64Url(blob.iv);
   const tag = this.fromBase64Url(blob.tag);
   const data = this.fromBase64Url(blob.data);

   const plaintext = await decryptAesGcm(key, iv, data, tag);
   return new TextDecoder().decode(plaintext);
 }
libs/sdk/src/auth/vault/auth-providers.registry.ts (2)

39-47: Add validation for options.providers array.

The constructor iterates over options.providers without validating that it's an array. If a malformed configuration is passed, the error message may be unclear.

♻️ Proposed enhancement
 constructor(options?: AuthProvidersVaultOptions) {
   this.defaultCacheTtl = options?.defaultCacheTtl ?? 3600000; // 1 hour

   if (options?.providers) {
+    if (!Array.isArray(options.providers)) {
+      throw new Error('AuthProvidersVaultOptions.providers must be an array');
+    }
     for (const provider of options.providers) {
       this.register(provider);
     }
   }
 }

55-63: Consider validating provider name is non-empty.

The register method doesn't validate that config.name is a non-empty string. Allowing empty or whitespace-only names could lead to confusion and lookup issues.

♻️ Proposed enhancement
 register<T extends Credential = Credential>(config: CredentialProviderConfig<T>): void {
+  if (!config.name || !config.name.trim()) {
+    throw new Error('Provider name must be a non-empty string');
+  }
   if (this.providers.has(config.name)) {
     throw new Error(`Credential provider "${config.name}" is already registered`);
   }

   const normalized = this.normalize(config);
   // Cast to base type for storage - generic is preserved on retrieval via get<T>()
   this.providers.set(config.name, normalized as unknown as NormalizedProviderConfig);
 }
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (2)

58-94: Minor timestamp inconsistency in credential metadata.

Lines 62 and 79 both call Date.now(), resulting in slightly different timestamps. The acquiredAt field (line 79) should represent when acquisition started or completed. Consider using startTime for consistency and accuracy.

♻️ Proposed fix
 private async doLoad<T extends Credential>(
   config: NormalizedProviderConfig<T>,
   context: CredentialFactoryContext,
 ): Promise<ResolvedCredential<T> | null> {
   const startTime = Date.now();
   this.logger?.debug(`Loading lazy credential: ${config.name}`);

   try {
     const credential = await config.factory({
       ...context,
       metadata: config.metadata,
     });

     if (!credential) {
       this.logger?.debug(`Lazy credential "${config.name}" returned null`);
       return null;
     }

     const resolved: ResolvedCredential<T> = {
       credential,
       providerId: config.name,
-      acquiredAt: Date.now(),
+      acquiredAt: startTime,
       expiresAt: this.extractExpiry(credential),
       isValid: true,
       scope: config.scope,
     };

     const duration = Date.now() - startTime;
     this.logger?.debug(`Loaded lazy credential "${config.name}" in ${duration}ms`);

     return resolved;
   } catch (error) {
     const duration = Date.now() - startTime;
     this.logger?.warn(`Failed to load lazy credential "${config.name}" after ${duration}ms:`, error);
     throw error;
   }
 }

103-144: Same timestamp inconsistency in refresh method.

Similar to doLoad, the refresh method has a timestamp inconsistency where acquiredAt (line 129) uses a new Date.now() call instead of startTime (line 107).

♻️ Proposed fix
 async refresh<T extends Credential>(
   config: NormalizedProviderConfig<T>,
   context: CredentialFactoryContext & { existingCredential: T },
 ): Promise<ResolvedCredential<T> | null> {
   const startTime = Date.now();
   this.logger?.debug(`Refreshing credential: ${config.name}`);

   try {
     let credential: T | null;

     if (config.refresh) {
       // Use dedicated refresh function
       credential = await config.refresh(context);
     } else {
       // Fall back to factory
       credential = await config.factory(context);
     }

     if (!credential) {
       this.logger?.debug(`Credential refresh "${config.name}" returned null`);
       return null;
     }

     const resolved: ResolvedCredential<T> = {
       credential,
       providerId: config.name,
-      acquiredAt: Date.now(),
+      acquiredAt: startTime,
       expiresAt: this.extractExpiry(credential),
       isValid: true,
       scope: config.scope,
     };

     const duration = Date.now() - startTime;
     this.logger?.debug(`Refreshed credential "${config.name}" in ${duration}ms`);

     return resolved;
   } catch (error) {
     const duration = Date.now() - startTime;
     this.logger?.warn(`Failed to refresh credential "${config.name}" after ${duration}ms:`, error);
     throw error;
   }
 }
libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (3)

47-97: Consider logging when provider name cannot be determined from error.

Line 86 falls back to 'unknown' when the provider name can't be extracted from the error. This could make debugging difficult if multiple providers fail. Consider logging a warning when this fallback occurs.

♻️ Proposed enhancement
       } else {
         const providerName = (result.reason as { providerName?: string })?.providerName || 'unknown';
+        if (providerName === 'unknown') {
+          this.logger?.warn('Failed to determine provider name from error:', result.reason);
+        }
         const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
         failed.set(providerName, error);
         this.logger?.warn(`Failed to load eager credential "${providerName}":`, error.message);
       }

102-116: Consider using a structured error wrapper instead of mutating error objects.

Lines 112-113 mutate the error object to add providerName. This approach can fail if the error is frozen/sealed and TypeScript doesn't track the added property. Consider wrapping in a structured error class.

♻️ Proposed enhancement

First, define an error class at the top of the file:

class ProviderLoadError extends Error {
  constructor(
    message: string,
    public readonly providerName: string,
    public readonly cause?: Error
  ) {
    super(message);
    this.name = 'ProviderLoadError';
  }
}

Then update the loadOne method:

 private async loadOne<T extends Credential>(
   config: NormalizedProviderConfig<T>,
   context: CredentialFactoryContext,
 ): Promise<T | null> {
   try {
     return await config.factory({
       ...context,
       metadata: config.metadata,
     });
   } catch (error) {
-    // Add provider name to error for identification
-    (error as { providerName?: string }).providerName = config.name;
-    throw error;
+    throw new ProviderLoadError(
+      `Failed to load credential for provider "${config.name}"`,
+      config.name,
+      error instanceof Error ? error : new Error(String(error))
+    );
   }
 }

Then update the error extraction in loadForSession:

       } else {
-        const providerName = (result.reason as { providerName?: string })?.providerName || 'unknown';
+        const providerName = result.reason instanceof ProviderLoadError 
+          ? result.reason.providerName 
+          : 'unknown';
         const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
         failed.set(providerName, error);
         this.logger?.warn(`Failed to load eager credential "${providerName}":`, error.message);
       }

121-134: Consider validating credential expiry when setting isValid.

Line 131 hardcodes isValid: true, but if the credential factory returns an already-expired credential (e.g., expiresAt is in the past), the resolved credential should reflect that. Consider checking expiry when setting the isValid flag.

♻️ Proposed fix
 private wrapCredential<T extends Credential>(
   providerId: string,
   credential: T,
   config: NormalizedProviderConfig<T>,
 ): ResolvedCredential<T> {
+  const expiresAt = this.extractExpiry(credential);
+  const isValid = !expiresAt || Date.now() < expiresAt;
+
   return {
     credential,
     providerId,
     acquiredAt: Date.now(),
-    expiresAt: this.extractExpiry(credential),
-    isValid: true,
+    expiresAt,
+    isValid,
     scope: config.scope,
   };
 }
libs/utils/src/crypto/key-persistence/schemas.ts (4)

28-32: Consider adding .strict() to prevent unexpected properties.

The baseKeyDataSchema doesn't use .strict(), which allows additional properties to pass validation. Per learnings, all Zod schemas should use .strict() for validation to prevent unexpected data from being accepted.

♻️ Add .strict() to schema
 const baseKeyDataSchema = z.object({
   kid: z.string().min(1),
   createdAt: z.number().positive().int(),
   version: z.number().positive().int(),
-});
+}).strict();

Based on learnings about using .strict() on all Zod schemas.


60-64: Consider adding .strict() to secretKeyDataSchema.

The schema extends baseKeyDataSchema but doesn't add .strict(). While it inherits from base, explicitly adding .strict() would ensure no unexpected properties are accepted at this level.

♻️ Add .strict() for stricter validation
 export const secretKeyDataSchema = baseKeyDataSchema.extend({
   type: z.literal('secret'),
   secret: z.string().min(1),
   bytes: z.number().positive().int(),
-});
+}).strict();

Based on learnings about using .strict() on Zod schemas.


69-76: Consider adding .strict() to asymmetricKeyDataSchema.

Similar to secretKeyDataSchema, consider adding .strict() to ensure no unexpected properties are accepted.

♻️ Add .strict() for stricter validation
 export const asymmetricKeyDataSchema = baseKeyDataSchema.extend({
   type: z.literal('asymmetric'),
   alg: asymmetricAlgSchema,
   privateKey: jsonWebKeySchema,
   publicJwk: z.object({
     keys: z.array(jsonWebKeySchema).min(1),
   }),
-});
+}).strict();

Based on learnings about using .strict() on Zod schemas.


137-143: Non-null assertion without verification.

Line 142 uses non-null assertion (validation.data!) after checking validation.valid. While this is logically safe, the coding guidelines discourage non-null assertions in favor of proper error handling.

♻️ Remove non-null assertion
 export function parseKeyData(data: unknown): AnyKeyData | null {
   const validation = validateKeyData(data);
-  if (!validation.valid) {
-    return null;
-  }
-  return validation.data!;
+  return validation.valid ? validation.data ?? null : null;
 }

Or use a type guard approach:

 export function parseKeyData(data: unknown): AnyKeyData | null {
   const validation = validateKeyData(data);
-  if (!validation.valid) {
+  if (!validation.valid || !validation.data) {
     return null;
   }
-  return validation.data!;
+  return validation.data;
 }

As per coding guidelines to avoid non-null assertions.

libs/sdk/src/auth/vault/credential-cache.ts (2)

140-147: Map modification during iteration is safe in JavaScript but consider consistency.

The invalidateByScope method correctly iterates and deletes entries. In JavaScript, deleting keys from a Map during for...of iteration is safe (deleted keys won't be visited again, and the iteration continues correctly). However, there's no eviction stat increment here unlike cleanup().

For consistency, consider incrementing evictions when invalidating by scope, or clarify in documentation that evictions only tracks TTL-based removals.

💡 Optional: Track evictions consistently
   invalidateByScope(scope: CredentialScope): void {
+    let removed = 0;
     for (const [key, entry] of this.cache) {
       if (entry.resolved.scope === scope) {
         this.cache.delete(key);
+        removed++;
       }
     }
     this.stats.size = this.cache.size;
+    // Optionally track: this.stats.evictions += removed;
   }

48-67: Duplicate credential expiration check.

The get() method checks credential expiration twice: once via isExpired() (line 49) which internally checks entry.resolved.expiresAt (line 210), and again explicitly at lines 58-64. The second check is redundant since isExpiredAt() already handles this case.

♻️ Remove redundant expiration check
   get<T extends Credential = Credential>(providerId: string): ResolvedCredential<T> | undefined {
     const entry = this.cache.get(providerId);

     if (!entry) {
       this.stats.misses++;
       return undefined;
     }

-    // Check TTL expiration
+    // Check TTL and credential expiration
     if (this.isExpired(entry)) {
       this.cache.delete(providerId);
       this.stats.size = this.cache.size;
       this.stats.misses++;
       this.stats.evictions++;
       return undefined;
     }

-    // Check credential expiration
-    if (entry.resolved.expiresAt && Date.now() >= entry.resolved.expiresAt) {
-      this.cache.delete(providerId);
-      this.stats.size = this.cache.size;
-      this.stats.misses++;
-      this.stats.evictions++;
-      return undefined;
-    }
-
     this.stats.hits++;
     return entry.resolved as ResolvedCredential<T>;
   }
libs/auth/src/session/storage/encrypted-typed-storage.ts (1)

273-285: Consider documenting key cleanup strategy.

The rotateKey method correctly adds new keys while retaining old ones for decryption. However, there's no method to remove deprecated keys from the keyMap. This may be intentional for backward compatibility, but consider:

  1. Documenting that old keys accumulate in memory
  2. Optionally adding a removeKey(kid) method for explicit cleanup after re-encryption is complete
libs/utils/src/storage/adapters/filesystem.ts (1)

130-134: Broad error suppression may hide configuration issues.

The catch block silently ignores all errors from mkdir, but the comment only mentions "Directory already exists". This could mask permission denied (EPERM), disk full (ENOSPC), or other filesystem errors that indicate misconfiguration.

♻️ Only ignore EEXIST errors
     // Try to set directory permissions (may not work on all platforms)
     try {
       await mkdir(this.baseDir, { recursive: true, mode: this.dirMode });
-    } catch {
-      // Directory already exists, ignore error
+    } catch (e) {
+      const error = e as NodeJS.ErrnoException;
+      // Only ignore if directory already exists
+      if (error.code !== 'EEXIST') {
+        // Log or handle other errors (permission issues, disk full, etc.)
+        // For now, we proceed as ensureDir already succeeded
+      }
     }
libs/auth/src/ui/templates.ts (1)

560-566: Consider documenting renderToHtml wrapper purpose.

The _options parameter is unused. If this is intentionally a no-op wrapper for API compatibility, consider adding a brief JSDoc note explaining when it might be removed or enhanced.

libs/auth/src/session/storage/storage-authorization-vault.ts (1)

429-454: Cleanup method has scalability concerns for large datasets.

The keys('*') call loads all keys into memory, which could cause issues with large vault datasets. The comment acknowledges this, but consider:

  1. Adding a limit parameter to process in batches
  2. Documenting the expected use case (e.g., "for small to medium datasets only")
  3. Implementing cursor-based iteration for production use
libs/auth/src/index.ts (1)

42-47: Add @deprecated JSDoc annotations to dev-key persistence exports in the barrel file.

These functions are marked as deprecated in the jwks module but lack deprecation notices at the final re-export level. IDE tooling reads JSDoc from the barrel export, so users importing from @frontmcp/auth won't receive deprecation warnings without annotations here. Add deprecation notices above the export block on lines 42-47.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @libs/auth/jest.config.cts:
- Around line 12-20: Update the Jest coverageThreshold block so the global
thresholds for statements, branches, functions, and lines are all set to 95;
modify the coverageThreshold/global entries (statements, branches, functions,
lines) in libs/auth/jest.config.cts to 95 to meet the documented requirement
before release.

In @libs/auth/src/session/authorization-vault.ts:
- Around line 1021-1030: The pending auth status update in completePendingAuth
is never persisted because authorizeApp reloads/writes the vault entry from
Redis; modify completePendingAuth (which calls get, updates entry.pendingAuths
and then authorizeApp) to persist the updated entry back to storage before
calling authorizeApp—e.g., after setting pendingAuth.status = 'completed' call
the vault save method (set/saveEntry/updateEntry) so the 'completed' status is
written to Redis, then call authorizeApp(vaultId, pendingAuth.appId).

In @libs/auth/src/session/storage/storage-authorization-vault.ts:
- Around line 381-389: The code uses a non-null assertion on entry.consent
(entry.consent!.selectedToolIds); instead add a proper guard: retrieve const
consent = entry.consent; if (!consent) return false (or handle as appropriate),
then replace both uses of entry.consent! with consent.selectedToolIds so
selectedToolIds is accessed only after the null check; ensure this guard covers
the branch that checks toolIds.some(...) and the later consentedToolIds logic.

In @libs/auth/src/session/token.vault.ts:
- Around line 28-35: The TokenVault constructor currently accepts any VaultKey
array and blindly inserts entries into this.keys and sets this.active; update it
to validate each VaultKey: ensure key.key is a Buffer/Uint8Array of exactly 32
bytes (throw an Error mentioning the offending kid if not), and check for
duplicate kid values before inserting into this.keys (throw if a duplicate kid
is encountered rather than silently overwriting). Also validate that the chosen
active key (this.active = keys[0]) itself passes the 32-byte check and exists in
the map after insertion.
- Around line 57-67: The decrypt method must enforce blob.exp: before decoding
or decrypting, check if blob.exp is present, parse/validate it as a numeric
timestamp, compare it to the current time, and if it is expired throw a clear
error (e.g. throw new Error(`vault_expired:${blob.kid}`) or similar). Place this
check at the start of decrypt (in function decrypt) so expired blobs are
rejected immediately; if blob.exp is missing treat it as non-expiring. Ensure
the error distinguishes expired vs unknown kid (keep the existing
vault_unknown_kid behavior).
🧹 Nitpick comments (17)
libs/auth/src/session/token.vault.ts (1)

37-40: Consider validating the key parameter for consistency.

The rotateTo method doesn't validate the input key, which could allow invalid keys (wrong size, missing properties) to be set as active. This is inconsistent with the validation proposed for the constructor.

♻️ Proposed validation for rotateTo
 rotateTo(k: VaultKey) {
+  if (!k.kid || typeof k.kid !== 'string') {
+    throw new Error('rotateTo: key must have a non-empty string kid');
+  }
+  if (!(k.key instanceof Uint8Array) || k.key.length !== 32) {
+    throw new Error(`rotateTo: key ${k.kid} must be a 32-byte Uint8Array for AES-256-GCM`);
+  }
   this.active = k;
   this.keys.set(k.kid, k.key);
 }
libs/auth/src/session/vault-encryption.ts (4)

125-132: Consider using delimited concatenation for IKM to prevent collisions.

Joining IKM parts without a delimiter can cause collisions. For example, jti="ab", vaultKey="cd" produces the same IKM as jti="abc", vaultKey="d". Using a delimiter (e.g., null byte \0) ensures distinct inputs produce distinct keys.

♻️ Proposed fix
     const ikmParts = [
       claims.jti,
       claims.vaultKey ?? '',
       claims.sub,
       claims.iat.toString(),
       new TextDecoder().decode(this.pepper),
     ];
-    const ikm = new TextEncoder().encode(ikmParts.join(''));
+    const ikm = new TextEncoder().encode(ikmParts.join('\0'));

157-165: Apply the same delimiter fix to deriveKeyFromToken.

This method has the same concatenation pattern without delimiters, sharing the collision risk noted for deriveKey.

♻️ Proposed fix
     const ikmParts = [
       claims.jti,
       claims.vaultKey ?? '',
       claims.sub,
       claims.iat.toString(),
       signature,
       new TextDecoder().decode(this.pepper),
     ];
-    const ikm = new TextEncoder().encode(ikmParts.join(''));
+    const ikm = new TextEncoder().encode(ikmParts.join('\0'));

33-44: Consider adding .strict() to Zod schemas for stricter validation.

The encryptedDataSchema and encryptedVaultEntrySchema don't use .strict(), which allows extra properties to pass validation silently. Based on learnings for uipack validation schemas, consider using .strict() for tighter input validation, especially for security-sensitive data structures.

♻️ Proposed fix for encryptedDataSchema
 export const encryptedDataSchema = z.object({
   /** Version for future algorithm changes */
   v: z.literal(1),
   /** Algorithm identifier */
   alg: z.literal('aes-256-gcm'),
   /** Initialization vector (base64) */
   iv: z.string(),
   /** Ciphertext (base64) */
   ct: z.string(),
   /** Authentication tag (base64) */
   tag: z.string(),
-});
+}).strict();

Also applies to: 281-304


254-257: Unsafe cast on JSON.parse result.

The decryptObject method casts the parsed JSON directly to T without validation. If the encrypted data was tampered with before encryption or the wrong key is used (producing garbage that happens to be valid JSON), this could return malformed data.

♻️ Consider accepting an optional schema for validation
-  async decryptObject<T>(encrypted: EncryptedData, key: Uint8Array): Promise<T> {
+  async decryptObject<T>(encrypted: EncryptedData, key: Uint8Array, schema?: z.ZodType<T>): Promise<T> {
     const plaintext = await this.decrypt(encrypted, key);
-    return JSON.parse(plaintext) as T;
+    const parsed = JSON.parse(plaintext);
+    return schema ? schema.parse(parsed) : (parsed as T);
   }
libs/sdk/src/auth/vault/auth-providers.vault.ts (4)

98-102: Unsafe type cast without validation.

The cast appCredential.credential as T assumes the stored credential matches the generic type, but this isn't validated. If the stored credential type doesn't match the expected type, this could cause runtime issues downstream.

♻️ Consider documenting the assumption or adding runtime validation
-      return appCredential.credential as T;
+      // Type safety: caller is responsible for ensuring T matches the stored credential type
+      // Consider passing a Zod schema for runtime validation in type-sensitive contexts
+      return appCredential.credential as T;

121-127: Inconsistent error handling: errors silently swallowed.

removeCredential logs a warning but doesn't throw or return a failure indicator, unlike storeCredential and refreshOAuthCredential which rethrow. This inconsistency could mask storage failures.

♻️ Consider consistent error handling
   async removeCredential(
     sessionId: string,
     providerId: string,
     scope: CredentialScope,
     userId?: string,
   ): Promise<void> {
     const vaultKey = this.buildVaultKey(sessionId, scope, userId);

     try {
       await this.baseVault.removeAppCredential(vaultKey, this.namespace, providerId);
       this.logger?.debug(`Removed credential "${providerId}"`);
     } catch (error) {
       this.logger?.warn(`Failed to remove credential "${providerId}":`, error);
+      throw error;
     }
   }

Alternatively, if silent failures are intentional (e.g., idempotent deletes), document this behavior in the JSDoc.


258-262: Default case duplicates session scope logic.

The default case on lines 260-261 repeats the session case behavior. This is defensive but could be simplified or made explicit with an exhaustive type check.

♻️ Use exhaustive check for type safety
       case 'session':
         return `${this.namespace}session:${sessionId}`;
-      default:
-        return `${this.namespace}session:${sessionId}`;
+      default: {
+        // Exhaustive check - should never reach here with valid CredentialScope
+        const _exhaustive: never = scope;
+        return `${this.namespace}session:${sessionId}`;
+      }
     }

268-278: Code duplication with extractCredentialExpiry in credential-helpers.ts.

This extractExpiry method duplicates the logic in libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts. Consider importing and reusing that helper to maintain DRY principles.

♻️ Proposed fix
+import { extractCredentialExpiry } from './credential-loaders/credential-helpers';
 
 // ... in class

-  /**
-   * Extract expiry time from credential
-   */
-  private extractExpiry(credential: Credential): number | undefined {
-    switch (credential.type) {
-      case 'oauth':
-      case 'oauth_pkce':
-      case 'bearer':
-      case 'service_account':
-        return credential.expiresAt;
-      default:
-        return undefined;
-    }
-  }
+  /**
+   * Extract expiry time from credential
+   */
+  private extractExpiry(credential: Credential): number | undefined {
+    return extractCredentialExpiry(credential);
+  }
libs/auth/src/session/storage/storage-authorization-vault.ts (2)

121-126: Silent failure on missing vault entry.

The update method logs a warning but silently returns when the entry is not found. This could mask bugs where callers expect an error on invalid IDs. Consider throwing or returning a boolean to indicate success/failure.

♻️ Consider throwing for explicit failure
   async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<void> {
     const entry = await this.get(id);
     if (!entry) {
-      console.warn(`[StorageAuthorizationVault] Update failed: vault entry not found for id ${id}`);
-      return;
+      throw new Error(`[StorageAuthorizationVault] Update failed: vault entry not found for id ${id}`);
     }

Alternatively, document this as intentional idempotent behavior if that's the design intent.


432-457: Scalability concern acknowledged in comments, but consider additional safeguards.

The cleanup implementation iterates all keys with keys('*'), which is noted as expensive. Consider adding a limit or cursor-based pagination to prevent memory issues with large datasets.

♻️ Consider adding a batch limit
   async cleanup(): Promise<void> {
     // For storage-based implementation, cleanup would need to iterate over all keys
     // This is expensive for large datasets, so we rely on in-document expiration checks
     // For production use, consider a scheduled cleanup job with cursor-based iteration
     const keys = await this.storage.keys('*');
     const now = Date.now();
+    
+    // Process in batches to limit memory usage
+    const BATCH_SIZE = 100;
+    const keysToProcess = keys.slice(0, BATCH_SIZE);
+    if (keys.length > BATCH_SIZE) {
+      console.warn(`[StorageAuthorizationVault] cleanup: processing first ${BATCH_SIZE} of ${keys.length} keys`);
+    }

-    for (const key of keys) {
+    for (const key of keysToProcess) {
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (1)

157-162: cancelAll() clears the map but doesn't reject pending promises.

Clearing the in-flight map allows new loads to start, but existing callers awaiting the old promises will still complete normally. If true cancellation is intended, consider also rejecting the pending promises or documenting the current behavior.

📝 Consider documenting the behavior
   /**
    * Cancel all in-flight loads (for cleanup)
+   *
+   * Note: This clears the deduplication map, allowing new loads to start.
+   * Existing callers awaiting previous loads will still complete normally.
+   * For true cancellation with AbortController, a more complex implementation would be needed.
    */
   cancelAll(): void {
     this.loading.clear();
   }
libs/sdk/src/auth/session/authorization-vault.ts (1)

169-170: Consider deprecating DSA key type.

DSA keys are deprecated in modern SSH implementations (OpenSSH disabled them by default in version 7.0). While this may be needed for legacy compatibility, consider adding a JSDoc comment noting that DSA is deprecated, or remove it if legacy support isn't required.

libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (1)

86-91: Fragile provider name extraction from rejected promises.

The provider name extraction relies on the error having been mutated by loadOne (line 114). If an error is thrown before reaching that mutation (e.g., during argument evaluation), providerName will be undefined and default to 'unknown'. This makes debugging harder.

Consider capturing the provider name before the async operation to ensure it's always available:

♻️ Proposed fix
     // Load all in parallel
     const results = await Promise.allSettled(
       eagerProviders.map(async (config) => {
-        const credential = await this.loadOne(config, context);
-        return { name: config.name, credential, config };
+        try {
+          const credential = await this.loadOne(config, context);
+          return { name: config.name, credential, config };
+        } catch (error) {
+          // Re-throw with provider name always attached
+          const err = error instanceof Error ? error : new Error(String(error));
+          (err as { providerName?: string }).providerName = config.name;
+          throw err;
+        }
       }),
     );

     // Process results
     for (const result of results) {
       if (result.status === 'fulfilled') {
         const { name, credential, config } = result.value;
         if (credential) {
           const resolved = this.wrapCredential(name, credential, config);
           loaded.set(name, resolved);
           this.cache.set(name, resolved, config.cacheTtl);
           this.logger?.debug(`Loaded eager credential: ${name}`);
         } else if (config.required) {
           failed.set(name, new Error(`Required credential "${name}" returned null`));
           this.logger?.warn(`Required eager credential "${name}" returned null`);
         }
       } else {
-        const providerName = (result.reason as { providerName?: string })?.providerName || 'unknown';
+        const providerName = (result.reason as { providerName?: string })?.providerName ?? 'unknown';
         const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
         failed.set(providerName, error);
         this.logger?.warn(`Failed to load eager credential "${providerName}":`, error.message);
       }
     }
libs/auth/src/session/authorization-vault.ts (3)

886-891: Consider typing the Redis client parameter.

The any type for the Redis client reduces type safety. Consider defining a minimal interface for the Redis operations used:

♻️ Proposed interface
+/** Minimal Redis client interface for vault storage */
+interface RedisVaultClient {
+  get(key: string): Promise<string | null>;
+  set(key: string, value: string): Promise<unknown>;
+  del(key: string): Promise<unknown>;
+}
+
 export class RedisAuthorizationVault implements AuthorizationVault {
   constructor(
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    private readonly redis: any,
+    private readonly redis: RedisVaultClient,
     private readonly namespace = 'vault:',
   ) {}

This would work with both ioredis and @vercel/kv (both listed as peerDependencies in libs/auth/package.json) while maintaining type safety.


1072-1075: Redis cleanup method is a placeholder.

The cleanup() method lacks implementation for the Redis vault. Consider adding a TODO comment with more detail or implementing the SCAN-based cleanup.

Would you like me to help implement the Redis cleanup using SCAN, or should I open an issue to track this?


932-938: Consider validating parsed JSON against schema.

The get method parses JSON and casts directly to AuthorizationVaultEntry without validation. If the stored data is malformed or has been tampered with, this could cause runtime errors or security issues.

♻️ Proposed fix with schema validation
   async get(id: string): Promise<AuthorizationVaultEntry | null> {
     const data = await this.redis.get(this.key(id));
     if (!data) return null;

-    const entry = JSON.parse(data) as AuthorizationVaultEntry;
-    return entry;
+    try {
+      const parsed = JSON.parse(data);
+      return authorizationVaultEntrySchema.parse(parsed);
+    } catch {
+      // Log error and return null for corrupted entries
+      return null;
+    }
   }

This adds defense-in-depth for data integrity and would catch schema drift or corruption issues early.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 806cd2a and 6de120e.

📒 Files selected for processing (22)
  • libs/auth/jest.config.cts
  • libs/auth/src/jwks/__tests__/jwks.utils.test.ts
  • libs/auth/src/jwks/jwks.utils.ts
  • libs/auth/src/session/authorization-vault.ts
  • libs/auth/src/session/storage/__tests__/storage-token-store.test.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
  • libs/auth/src/session/storage/storage-token-store.ts
  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/auth/src/ui/__tests__/base-layout.advanced.test.ts
  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
  • libs/auth/src/ui/__tests__/templates.test.ts
  • libs/auth/src/ui/base-layout.ts
  • libs/auth/src/ui/templates.ts
  • libs/sdk/src/auth/session/authorization-vault.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • libs/auth/src/session/storage/tests/storage-token-store.test.ts
  • libs/auth/src/jwks/tests/jwks.utils.test.ts
  • libs/auth/src/ui/tests/base-layout.advanced.test.ts
  • libs/auth/src/session/storage/storage-token-store.ts
  • libs/auth/src/ui/tests/templates.test.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/ui/base-layout.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
  • libs/sdk/src/auth/session/authorization-vault.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
  • libs/auth/src/jwks/jwks.utils.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/auth/src/ui/templates.ts
  • libs/auth/src/session/authorization-vault.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Include instanceof checks in tests for error classes to verify proper error hierarchy

Files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts
  • libs/auth/jest.config.cts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/ui/base-layout.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
  • libs/sdk/src/auth/session/authorization-vault.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
  • libs/auth/src/jwks/jwks.utils.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/auth/src/ui/templates.ts
  • libs/auth/src/session/authorization-vault.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
  • libs/sdk/src/auth/session/authorization-vault.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
  • libs/sdk/src/auth/session/authorization-vault.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
🧠 Learnings (30)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)

Applied to files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/jest.config.cts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,utils}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test HTML escaping for user-provided content to prevent XSS vulnerabilities

Applied to files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
  • libs/auth/src/ui/base-layout.ts
  • libs/auth/src/ui/templates.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/*.{test,spec}.{ts,tsx,js,jsx} : Every component and utility must test invalid inputs and edge cases

Applied to files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : SSR components must be tested for correct HTML output and client-side hydration must be tested separately

Applied to files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : Use React Testing Library for component tests and include SSR/hydration tests for all interactive components

Applied to files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/jest.config.cts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src,tests}/**/*.{ts,tsx,js,jsx} : Maintain 95%+ code coverage across statements, branches, functions, and lines

Applied to files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/jest.config.cts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : Test coverage must be 95%+ across statements, branches, functions, and lines

Applied to files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/jest.config.cts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : Do not skip SSR/hydration testing for components; all interactive widgets must be tested in both rendering contexts

Applied to files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/auth/src/ui/__tests__/templates.advanced.test.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/auth/src/ui/__tests__/base-layout.test.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/auth/src/ui/base-layout.ts
  • libs/auth/src/ui/templates.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rate

Applied to files:

  • libs/auth/jest.config.cts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)

Applied to files:

  • libs/auth/jest.config.cts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.test.ts : Tests must achieve 95%+ code coverage across all metrics; use `MockStore` implementing `RememberStoreInterface` for RememberPlugin testing

Applied to files:

  • libs/auth/jest.config.cts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Applied to files:

  • libs/sdk/src/auth/vault/credential-loaders/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files

Applied to files:

  • libs/sdk/src/auth/vault/credential-loaders/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/auth/src/session/vault-encryption.ts
  • libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/auth/src/session/vault-encryption.ts
  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/auth/src/session/vault-encryption.ts
  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{theme,build,bundler}/**/*.{ts,tsx,js,jsx} : Do not hard-code CDN URLs - use theme.cdn configuration instead

Applied to files:

  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,build}/**/*.{ts,tsx} : Use mdxClientRenderer for CDN-based MDX rendering without bundled React

Applied to files:

  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{build,bundler}/**/*.{ts,tsx,js,jsx} : For server-side MDX rendering with bundled React, use frontmcp/ui/renderers instead of frontmcp/uipack/renderers

Applied to files:

  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,base-template}/**/*.{ts,tsx,js,jsx} : Never skip HTML escaping for user-provided content in renderers and templates

Applied to files:

  • libs/auth/src/ui/base-layout.ts
  • libs/auth/src/ui/templates.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/components/**/*.{ts,tsx} : HTML string component functions (button, card, alert, badge) must accept component-specific props and return HTML strings

Applied to files:

  • libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{validation,**}/**/*.{ts,tsx} : Use `.strict()` on all Zod schemas for validation

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.types.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/auth/session/authorization-vault.ts
  • libs/auth/src/jwks/jwks.utils.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,build}/**/*.{ts,tsx} : Use htmlRenderer for HTML template rendering without React dependency

Applied to files:

  • libs/auth/src/ui/templates.ts
🧬 Code graph analysis (7)
libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts (2)
libs/auth/src/session/authorization-vault.ts (1)
  • Credential (247-247)
libs/sdk/src/auth/session/authorization-vault.ts (1)
  • Credential (247-247)
libs/auth/src/ui/__tests__/templates.advanced.test.ts (1)
libs/auth/src/ui/templates.ts (9)
  • buildConsentPage (97-123)
  • AppAuthCard (32-43)
  • buildIncrementalAuthPage (188-249)
  • buildFederatedLoginPage (254-341)
  • ProviderCard (48-61)
  • buildToolConsentPage (346-466)
  • ToolCard (66-77)
  • buildLoginPage (471-526)
  • buildErrorPage (531-553)
libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (4)
libs/sdk/src/auth/vault/auth-providers.types.ts (1)
  • ResolvedCredential (71-84)
libs/sdk/src/auth/vault/auth-providers.registry.ts (2)
  • AuthProvidersRegistry (35-186)
  • NormalizedProviderConfig (21-30)
libs/sdk/src/auth/vault/credential-cache.ts (1)
  • CredentialCache (25-242)
libs/sdk/src/auth/session/authorization-vault.ts (1)
  • Credential (247-247)
libs/sdk/src/auth/vault/auth-providers.types.ts (1)
libs/sdk/src/auth/session/authorization-vault.ts (2)
  • Credential (247-247)
  • AuthorizationVault (386-534)
libs/sdk/src/auth/vault/auth-providers.vault.ts (3)
libs/sdk/src/auth/vault/index.ts (3)
  • AuthProvidersVault (81-81)
  • CredentialScope (46-46)
  • AUTH_PROVIDERS_VAULT (81-81)
libs/sdk/src/auth/session/authorization-vault.ts (3)
  • AuthorizationVault (386-534)
  • Credential (247-247)
  • AppCredential (285-285)
libs/sdk/src/auth/vault/auth-providers.types.ts (1)
  • CredentialScope (22-22)
libs/auth/src/ui/templates.ts (1)
libs/auth/src/ui/base-layout.ts (4)
  • escapeHtml (157-157)
  • wideLayout (357-366)
  • centeredCardLayout (340-352)
  • extraWideLayout (371-380)
libs/auth/src/session/authorization-vault.ts (3)
libs/sdk/src/auth/session/authorization-vault.ts (15)
  • credentialTypeSchema (34-45)
  • oauthCredentialSchema (56-70)
  • apiKeyCredentialSchema (75-85)
  • basicAuthCredentialSchema (90-98)
  • bearerCredentialSchema (103-109)
  • privateKeyCredentialSchema (114-128)
  • mtlsCredentialSchema (133-143)
  • customCredentialSchema (148-156)
  • sshKeyCredentialSchema (161-175)
  • serviceAccountCredentialSchema (180-198)
  • pkceOAuthCredentialSchema (203-219)
  • credentialSchema (224-235)
  • OAuthCredential (237-237)
  • Credential (247-247)
  • key (893-895)
libs/utils/src/crypto/index.ts (1)
  • randomUUID (48-50)
libs/auth/src/session/storage/storage-authorization-vault.ts (1)
  • key (467-469)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Build Libraries
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (47)
libs/auth/jest.config.cts (1)

1-11: LGTM - Jest configuration is well-structured.

The basic Jest setup is appropriate for a Node.js library, with correct ts-jest transform configuration and proper handling of the jose ESM module via transformIgnorePatterns.

libs/auth/src/jwks/jwks.utils.ts (1)

21-21: Good improvement to type safety!

The added !Array.isArray(obj) check ensures the runtime behavior correctly matches the Record<string, unknown> return type. JWT payloads are typically objects (per RFC 7519), so filtering out arrays aligns with standards and prevents type mismatches. The comprehensive test suite, including a dedicated test case for array payload rejection (lines 100-105), confirms this change is intentional and safe.

libs/auth/src/session/token.vault.ts (2)

8-8: LGTM! Correct use of @frontmcp/utils for cryptographic operations.

The imports follow the coding guidelines and learnings by using @frontmcp/utils instead of node:crypto directly.


42-55: LGTM! Encryption logic is correct.

The implementation correctly:

  • Generates a 12-byte IV for AES-GCM mode
  • Uses the active key for encryption
  • Constructs a well-formed EncBlob with proper base64url encoding
libs/auth/src/session/vault-encryption.ts (1)

23-24: LGTM! Proper use of @frontmcp/utils for cryptographic operations.

Correctly imports cryptographic primitives from the centralized utils package rather than using node:crypto directly, aligning with the codebase guidelines.

libs/sdk/src/auth/vault/auth-providers.types.ts (3)

56-62: LGTM! Schema uses .strict() for validation.

The getCredentialOptionsSchema properly uses .strict() to reject unknown properties, aligning with validation best practices.


188-202: Reasonable use of z.any() for function schemas with documented justification.

The comment on line 195 explains the rationale for using z.any() for function properties (Zod 4 compatibility). While coding guidelines discourage any types, runtime validation of function signatures is not practical with Zod, and the justification is documented. Consider adding a runtime type check in the consuming code if stricter validation is needed.


296-305: LGTM! Well-structured options schema with sensible defaults.

The authProvidersVaultOptionsSchema uses .strict() and provides reasonable defaults for optional configuration fields.

libs/auth/src/session/storage/storage-authorization-vault.ts (1)

22-23: LGTM! Proper use of @frontmcp/utils for UUID generation.

Correctly imports randomUUID from the centralized utils package per coding guidelines.

libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts (1)

15-25: LGTM! Clean, focused utility function.

The extractCredentialExpiry helper is well-documented and handles the credential type switch appropriately. This is the canonical implementation that should be reused by other files (like auth-providers.vault.ts).

libs/sdk/src/auth/vault/credential-loaders/index.ts (1)

5-7: LGTM! Clean barrel exports.

Properly exports public APIs through a single entry point, following the codebase pattern for barrel exports.

libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (2)

34-54: LGTM! Well-implemented in-flight deduplication.

The load method correctly handles concurrent requests for the same provider by returning the existing promise, and properly cleans up the in-flight map in the finally block regardless of success or failure.


77-84: LGTM! Consistent use of extractCredentialExpiry helper.

Both doLoad and refresh methods correctly use the shared extractCredentialExpiry helper to populate the expiresAt field, maintaining consistency with the credential-helpers module.

Also applies to: 127-134

libs/sdk/src/auth/session/authorization-vault.ts (2)

42-44: LGTM! New credential types are well-integrated.

The new credential types (ssh_key, service_account, oauth_pkce) are properly added to the enum and included in the discriminated union. The schema definitions follow consistent patterns with existing credential types.

Also applies to: 232-234


857-873: LGTM! OAuth PKCE refresh support is correctly implemented.

The refreshOAuthCredential method now properly handles both oauth and oauth_pkce credential types, which share the same token structure. The type narrowing is sufficient since both schemas define accessToken, refreshToken, and expiresAt fields.

libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (2)

103-117: LGTM with the suggested refactor above.

The loadOne method correctly calls the factory with merged context and metadata. The error mutation here becomes unnecessary if the refactor above is applied.


119-135: LGTM!

The wrapCredential method correctly constructs a ResolvedCredential object with proper metadata. The use of extractCredentialExpiry ensures consistent expiry extraction across credential types.

libs/auth/src/session/authorization-vault.ts (1)

1-1211: Remove duplicate authorization-vault.ts implementation from SDK and re-export from @frontmcp/auth.

The file libs/sdk/src/auth/session/authorization-vault.ts duplicates libs/auth/src/session/authorization-vault.ts (~1200 lines). The @frontmcp/auth package already exports InMemoryAuthorizationVault, RedisAuthorizationVault, and all credential/vault schemas as its public API.

The SDK should import and re-export these from @frontmcp/auth in libs/sdk/src/auth/session/index.ts instead of maintaining a duplicate implementation. This ensures a single source of truth and prevents divergence between the auth package and SDK.

⛔ Skipped due to learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
libs/auth/src/ui/base-layout.ts (7)

37-46: LGTM! Centralized CDN configuration.

The CDN configuration is appropriately exported as a public constant, allowing consumers to reference or override these URLs if needed. This aligns with the pattern of centralizing external resource URLs.


56-102: LGTM! Well-structured theme configuration types.

The theme interfaces provide good extensibility through index signatures while defining common color and font properties. All properties are appropriately optional, allowing flexible theme customization.


107-120: LGTM! Sensible default theme.

The default theme provides a well-considered color palette and font stack with appropriate fallbacks.


162-189: LGTM! Clean CSS theme builder.

The internal buildThemeCss function correctly transforms theme configuration into CSS custom properties, filtering out undefined values and maintaining proper formatting.


215-281: LGTM! Comprehensive HTML document builder with proper escaping.

The baseLayout function correctly:

  • Merges themes with appropriate precedence
  • Conditionally includes CDN resources
  • Escapes all user-provided text (title, description, bodyClass)

Note: The content parameter is intentionally not escaped, as it's expected to contain pre-built HTML from template builders.


303-324: LGTM! Well-designed layout factory.

The createLayout factory correctly implements deep theme merging with appropriate nullish coalescing for custom properties.


333-380: LGTM! Useful preset layouts.

The preset layout functions provide convenient wrappers for common UI patterns, each with appropriate styling for their intended use cases.

libs/auth/src/ui/__tests__/base-layout.test.ts (6)

16-67: LGTM! Comprehensive HTML escaping tests with XSS coverage.

The test suite thoroughly covers HTML special character escaping and includes multiple XSS attack vectors, ensuring robust security testing.


69-79: LGTM! CDN configuration tests.

Tests verify the expected CDN URLs are correctly configured.


81-97: LGTM! Default theme tests.

Tests verify the default theme has expected color and font definitions.


99-246: LGTM! Comprehensive baseLayout tests.

The test suite thoroughly covers:

  • HTML document structure validation
  • XSS protection through escaping
  • Conditional resource inclusion
  • Theme merging and customization
  • All configuration options

Excellent edge case coverage.


248-299: LGTM! createLayout factory tests.

Tests cover the factory pattern, option overriding, and deep theme merging behavior.


301-359: LGTM! Layout wrapper tests.

Tests cover all preset layout functions and verify their specific styling characteristics.

libs/auth/src/ui/__tests__/templates.advanced.test.ts (7)

19-105: LGTM! Comprehensive consent page edge case tests.

Tests cover important edge cases including:

  • Empty/missing scopes
  • Multiple apps
  • XSS protection
  • Long strings

Excellent boundary condition testing.


107-131: LGTM! Incremental auth edge case tests.

Tests cover optional fields and XSS protection for the incremental authorization flow.


133-235: LGTM! Comprehensive federated login edge case tests.

Tests cover:

  • Optional provider fields
  • Multiple providers and app IDs
  • Correct checkbox pre-selection logic

Excellent coverage of provider selection scenarios.


237-324: LGTM! Comprehensive tool consent edge case tests.

Tests cover:

  • Optional tool descriptions
  • User info display logic
  • Multi-app tool grouping
  • XSS protection for user data

Excellent edge case coverage.


326-367: LGTM! Login page edge case tests.

Tests cover scope parsing with special characters, multiple scopes, and XSS protection for client names.


369-417: LGTM! Error page edge case tests.

Tests cover:

  • Empty error/description fields
  • Long error codes
  • Multi-line descriptions
  • HTML entity escaping

Comprehensive edge case coverage.


420-476: LGTM! Layout variation tests.

Tests verify each template function uses the appropriate layout wrapper by checking for layout-specific CSS classes.

libs/auth/src/ui/templates.ts (9)

32-77: LGTM! Well-defined template data types.

The interface definitions clearly separate required and optional fields, with appropriate types for each property.


97-123: LGTM! Consent page builder with proper escaping.

The function correctly escapes the client name and uses the appropriate wide layout for multi-app display.


128-182: LGTM! Internal card builder with comprehensive escaping.

The buildAppCardHtml function properly escapes all user-provided data including:

  • App metadata (ID, name, description, icon URL)
  • Scopes array
  • Security tokens and callback paths

Excellent XSS protection.


188-249: LGTM! Incremental auth page with proper escaping.

All dynamic content (tool ID, app metadata, session hint, callback path) is correctly escaped, and the centered card layout is appropriate for this single-app authorization flow.


254-341: LGTM! Federated login page with comprehensive escaping.

All provider metadata, client information, and form parameters are properly escaped. The wide layout is appropriate for the multi-provider selection interface.


346-466: LGTM! Tool consent page with thorough escaping.

All tool metadata, user information, and form parameters are properly escaped. The extra-wide layout is appropriate for displaying multiple tools grouped by app.


471-526: LGTM! Login page with proper scope handling and escaping.

Scopes are correctly split and individually escaped, and all other dynamic content (client name, callback path, pending auth ID) is properly escaped. Centered card layout is appropriate for the login form.


531-553: LGTM! Error page with proper escaping.

Both error code and description are correctly escaped, and the centered card layout is appropriate for error display.


563-565: LGTM! Simple compatibility wrapper.

The renderToHtml function serves as a no-op compatibility layer, appropriately using underscore prefix for the unused options parameter.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In @libs/auth/src/session/authorization-vault.ts:
- Around line 1171-1174: The Redis implementation uses a non-null assertion on
entry.consent (in the block that returns toolIds.some(...)) — replace the
assertion with a safe check: ensure entry.consent exists before accessing
selectedToolIds (e.g., grab const consent = entry.consent; if (!consent) return
false; then use consent.selectedToolIds.includes(toolId) or use optional
chaining) so you avoid the bang operator and potential runtime errors in the
authorization-vault Redis path.
- Around line 932-938: The get method parses Redis data with JSON.parse without
handling parse errors or validating the resulting object against the
AuthorizationVaultEntry shape; wrap the JSON.parse(this.redis.get(this.key(id)))
in a try/catch to return null (and optionally log a warning) on parse failure,
then validate the parsed object against the expected schema (e.g., a
validateAuthorizationVaultEntry or isAuthorizationVaultEntry helper) before
casting to AuthorizationVaultEntry and returning it; if validation fails, treat
it like missing data (return null) to avoid runtime exceptions.
- Around line 829-832: The non-null assertion on entry.consent should be
removed; after the existing guard that narrows entry.consent (the check above
line 825), store it in a local const (e.g., const consent = entry.consent) and
then use consent.selectedToolIds.includes(toolId) in the toolIds.some callback
(or, alternatively, use optional chaining like entry.consent?.selectedToolIds
but preferred is the local variable after the guard) so no `!` is needed.

In @libs/auth/src/session/token.vault.ts:
- Around line 57-70: The encrypt method calls encryptAesGcm with the wrong
argument order causing IV/plaintext mixup; update the encrypt function to call
encryptAesGcm(this.active.key, new TextEncoder().encode(plaintext), iv) (i.e.,
pass key, then plaintext bytes, then iv) so the AES-GCM API is used correctly,
keeping the rest of the returned EncBlob (alg, kid, iv, tag, data, exp, meta)
unchanged.
- Around line 52-55: rotateTo currently accepts any VaultKey and sets
this.active and this.keys without validating the key material, risking runtime
encryption errors; update rotateTo(VaultKey) to validate that k.key is a
Uint8Array of exactly 32 bytes (same constraint enforced in the constructor),
throw or reject with a clear error if invalid, and only set this.active and
this.keys.set(k.kid, k.key) after the validation passes; reference the VaultKey
type, rotateTo method, this.active, and this.keys when making the change so
behavior matches the constructor's key validation.
🧹 Nitpick comments (4)
libs/auth/src/session/authorization-vault.ts (3)

592-597: Inconsistent error handling for missing vault entries.

The update method silently returns when the vault entry is not found, but createPendingAuth throws an error for the same condition. Consider consistent error handling or explicit documentation of the silent-fail behavior.

Option: Throw or return a boolean indicating success
-  async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<void> {
+  async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<boolean> {
     const entry = this.vaults.get(id);
-    if (!entry) return;
+    if (!entry) return false;

     Object.assign(entry, updates, { lastAccessAt: Date.now() });
+    return true;
   }

718-731: Unused variable and inconsistent cleanup behavior.

  1. The id variable on line 721 is unused.
  2. The comment on line 727 says "Keep for audit trail" but the filter removes non-pending auths, contradicting the comment.
Fix unused variable and clarify logic
   async cleanup(): Promise<void> {
     const now = Date.now();

-    for (const [id, entry] of this.vaults) {
+    for (const entry of this.vaults.values()) {
       // Clean up expired pending auths
       entry.pendingAuths = entry.pendingAuths.filter((p) => {
         if (now > p.expiresAt && p.status === 'pending') {
           p.status = 'expired';
         }
-        // Keep for audit trail, or remove completely if desired
+        // Remove completed/cancelled/expired auths
         return p.status === 'pending';
       });
     }
   }

885-894: any type used for Redis client.

The any type for the Redis client is understandable given the variety of Redis client libraries, but consider defining a minimal interface for the required Redis operations (get, set, del) to provide some type safety.

Define minimal Redis client interface
+/** Minimal Redis client interface */
+interface RedisClient {
+  get(key: string): Promise<string | null>;
+  set(key: string, value: string): Promise<unknown>;
+  del(key: string): Promise<unknown>;
+}

 export class RedisAuthorizationVault implements AuthorizationVault {
   constructor(
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    private readonly redis: any,
+    private readonly redis: RedisClient,
     private readonly namespace = 'vault:',
   ) {}
libs/auth/src/session/storage/storage-authorization-vault.ts (1)

293-307: Redundant storage reads in addAppCredential.

The method fetches the entry on line 294, but shouldStoreCredential (called on line 298) fetches the same entry again. Consider refactoring to avoid the duplicate read.

Pass entry to avoid duplicate read
   async addAppCredential(vaultId: string, credential: AppCredential): Promise<void> {
     const entry = await this.get(vaultId);
     if (!entry) return;

     // Check if we should store based on consent
-    const shouldStore = await this.shouldStoreCredential(vaultId, credential.appId);
+    const shouldStore = this.shouldStoreCredentialForEntry(entry, credential.appId);
     if (!shouldStore) {
       return;
     }

     const key = this.credentialKey(credential.appId, credential.providerId);
     entry.appCredentials[key] = credential;
     entry.lastAccessAt = Date.now();
     await this.storage.set(this.key(vaultId), entry);
   }

+  private shouldStoreCredentialForEntry(entry: AuthorizationVaultEntry, appId: string, toolIds?: string[]): boolean {
+    const consent = entry.consent;
+    if (!consent?.enabled) {
+      return true;
+    }
+    if (toolIds && toolIds.length > 0) {
+      return toolIds.some((toolId) => consent.selectedToolIds.includes(toolId));
+    }
+    return consent.selectedToolIds.some((toolId) => toolId.startsWith(`${appId}:`));
+  }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5dcf4e0 and b245b58.

📒 Files selected for processing (5)
  • libs/auth/jest.config.cts
  • libs/auth/src/session/authorization-vault.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
  • libs/auth/src/session/token.vault.ts
  • libs/utils/src/storage/errors.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/utils/src/storage/errors.ts
  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
  • libs/auth/src/session/authorization-vault.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/utils/src/storage/errors.ts
  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
  • libs/auth/jest.config.cts
  • libs/auth/src/session/authorization-vault.ts
🧠 Learnings (20)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx,js,jsx} : Do not expose internal error details in public APIs - use sanitized error messages

Applied to files:

  • libs/utils/src/storage/errors.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Do not expose internal error details in public-facing error messages

Applied to files:

  • libs/utils/src/storage/errors.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errors

Applied to files:

  • libs/utils/src/storage/errors.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Avoid non-null assertions (`!`) - use proper error handling and type guards instead

Applied to files:

  • libs/auth/src/session/storage/storage-authorization-vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rate

Applied to files:

  • libs/auth/jest.config.cts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)

Applied to files:

  • libs/auth/jest.config.cts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : Test coverage must be 95%+ across statements, branches, functions, and lines

Applied to files:

  • libs/auth/jest.config.cts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)

Applied to files:

  • libs/auth/jest.config.cts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src,tests}/**/*.{ts,tsx,js,jsx} : Maintain 95%+ code coverage across statements, branches, functions, and lines

Applied to files:

  • libs/auth/jest.config.cts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.test.ts : Tests must achieve 95%+ code coverage across all metrics; use `MockStore` implementing `RememberStoreInterface` for RememberPlugin testing

Applied to files:

  • libs/auth/jest.config.cts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : Use React Testing Library for component tests and include SSR/hydration tests for all interactive components

Applied to files:

  • libs/auth/jest.config.cts
🧬 Code graph analysis (2)
libs/utils/src/storage/errors.ts (2)
libs/auth/src/session/authorization-vault.ts (1)
  • key (892-894)
libs/auth/src/session/storage/storage-authorization-vault.ts (1)
  • key (468-470)
libs/auth/src/session/token.vault.ts (1)
libs/utils/src/crypto/index.ts (5)
  • randomBytes (57-62)
  • encryptAesGcm (113-125)
  • base64urlEncode (182-197)
  • base64urlDecode (202-220)
  • decryptAesGcm (135-146)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Package E2E (Verdaccio)
🔇 Additional comments (16)
libs/utils/src/storage/errors.ts (1)

41-45: LGTM! Formatting improvements enhance readability.

The multi-line constructor parameter formatting is consistent across all affected error classes and improves code readability without changing any runtime behavior or public API signatures.

Also applies to: 65-70, 89-93, 114-117, 132-135, 153-156

libs/auth/jest.config.cts (1)

1-20: Rename file to jest.config.ts to match monorepo conventions.

The Jest configuration content is well-structured and coverage thresholds at 95% correctly enforce repository standards. However, the file uses .cts extension while all 17 other Jest config files in the monorepo use .ts extension. Rename jest.config.cts to jest.config.ts for consistency.

  • Coverage thresholds at 95% align with repository requirements
  • transformIgnorePatterns correctly handles the jose ESM module
  • Test environment node is appropriate for auth utilities
⛔ Skipped due to learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Do not use prefixes like 'PT-001' in test names
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : Test coverage must be 95%+ across statements, branches, functions, and lines
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src,tests}/**/*.{ts,tsx,js,jsx} : Maintain 95%+ code coverage across statements, branches, functions, and lines
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.test.ts : Tests must achieve 95%+ code coverage across all metrics; use `MockStore` implementing `RememberStoreInterface` for RememberPlugin testing
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Configure approval requirements on tools using the `approval` decorator property with fields: `required`, `defaultScope`, `category`, `riskLevel`, and `approvalMessage`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : Use React Testing Library for component tests and include SSR/hydration tests for all interactive components
libs/auth/src/session/token.vault.ts (4)

1-8: LGTM!

The imports correctly use @frontmcp/utils for cryptographic operations (encryptAesGcm, decryptAesGcm, randomBytes, base64urlEncode, base64urlDecode), which aligns with the coding guidelines and retrieved learnings.


10-20: LGTM!

The EncBlob and VaultKey type definitions are well-structured. The EncBlob envelope correctly includes all necessary fields for AES-256-GCM: algorithm identifier, key ID for rotation support, IV, authentication tag, ciphertext, and optional expiration/metadata.


28-50: LGTM!

The constructor properly validates:

  • Non-empty keys array
  • Each key is exactly 32 bytes (required for AES-256-GCM)
  • No duplicate kid values

This defensive validation prevents runtime errors during encryption operations.


72-90: LGTM!

The decrypt method correctly:

  • Checks expiration before attempting decryption (fail-fast)
  • Validates the kid exists in the key map
  • Uses the correct argument order for decryptAesGcm(key, ciphertext, iv, tag)
  • Properly decodes base64url fields before passing to the decryption function
libs/auth/src/session/authorization-vault.ts (4)

24-26: LGTM!

Clean imports using @frontmcp/utils for UUID generation as per coding guidelines.


34-248: LGTM! Well-structured credential schemas.

Good use of Zod discriminated union for type-safe credential handling. The schema coverage is comprehensive and follows Zod 4 best practices.


344-371: LGTM! Comprehensive vault entry schema.

The authorizationVaultEntrySchema provides a well-structured model for session state with appropriate defaults for collections.


386-534: LGTM! Well-designed interface.

The AuthorizationVault interface provides a comprehensive API surface with clear documentation for each method.

libs/auth/src/session/storage/storage-authorization-vault.ts (6)

22-34: LGTM! Clean imports and type usage.

Good use of type imports for interfaces and proper schema import for validation.


69-80: LGTM! Good defensive design.

The constructor handles both StorageAdapter and NamespacedStorage appropriately, with optional schema validation and graceful handling of invalid data.


121-134: Good observability improvement over other implementations.

The warning log on line 124 when update fails is a good practice that the other implementations lack. Consider adding similar logging to InMemoryAuthorizationVault and RedisAuthorizationVault for consistency.


233-250: Good optimization by inlining authorization logic.

Unlike the Redis implementation which calls authorizeApp separately (requiring another read-write cycle), this implementation inlines the logic to complete the pending auth and authorize the app in a single write. This is more efficient.


464-470: The TypedStorage.raw property exists and is correctly implemented. No issues found.

The getter is properly defined at lines 193-195 of typed-storage.ts and returns the underlying StorageAdapter | NamespacedStorage. The key() method correctly uses this property to determine whether to apply namespace prefixing.


433-458: The cleanup method correctly uses keys() which is supported by all storage backends.

The StorageAdapter interface requires all implementations to provide the keys() method with glob-pattern support. All storage adapters (memory, Redis, Vercel KV, Upstash) implement this method. The performance concern for large datasets is already documented in the code comments.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @libs/auth/src/session/authorization-vault.ts:
- Around line 718-731: In cleanup() the loop currently destructures [id, entry]
from this.vaults but never uses id; change the loop to iterate over the map
values instead (e.g., for (const entry of this.vaults.values())) so only entry
is declared, then keep the existing logic that filters entry.pendingAuths by
p.expiresAt and p.status; alternatively, if you prefer to keep the key for
future logging, rename id to _id to mark it as intentionally unused.
- Around line 932-943: In get(id: string) you currently JSON.parse and cast to
AuthorizationVaultEntry, which skips runtime checks; replace the cast with Zod
validation: parse the string, run authorizationVaultEntrySchema.safeParse (or
parse with try/catch) to validate the shape, return the parsed value on success,
and on validation failure treat it as a missing/corrupted entry (return null)
and optionally remove the key with this.redis.del(this.key(id)); keep using
this.redis.get and this.key(id) to locate the data and ensure the
authorizationVaultEntrySchema is imported where get is defined.
🧹 Nitpick comments (3)
libs/auth/src/session/authorization-vault.ts (3)

592-597: Consider returning success status or throwing on missing entries.

The update method (and similar methods like updateConsent, authorizeApp) silently returns when the entry is not found. While this provides idempotent behavior, it can mask bugs where callers operate on non-existent vaults. Consider either:

  • Returning a boolean indicating success
  • Throwing an error for missing entries (consistent with createPendingAuth)

This would improve debuggability without changing the current interface if you opt for the return value approach.


885-890: Define a minimal Redis interface instead of any.

Per coding guidelines, any types should be avoided. Define a minimal interface for the required Redis operations to maintain type safety while allowing different Redis client implementations:

♻️ Suggested interface
export interface RedisLike {
  get(key: string): Promise<string | null>;
  set(key: string, value: string): Promise<unknown>;
  del(key: string): Promise<unknown>;
}

export class RedisAuthorizationVault implements AuthorizationVault {
  constructor(
    private readonly redis: RedisLike,
    private readonly namespace = 'vault:',
  ) {}
  // ...
}

1082-1085: Consider implementing or documenting the cleanup placeholder.

The cleanup() method is a stub. If this is intentional for the initial release, consider adding a TODO comment or documentation explaining the expected behavior and limitations. Redis SCAN-based cleanup requires careful implementation to avoid blocking operations.

Would you like me to help draft a SCAN-based cleanup implementation for expired pending auths?

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b245b58 and 6aa5015.

📒 Files selected for processing (4)
  • libs/auth/src/session/authorization-vault.ts
  • libs/auth/src/session/token.vault.ts
  • libs/plugins/project.json
  • libs/plugins/tsconfig.lib.json
🧰 Additional context used
📓 Path-based instructions (2)
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/plugins/tsconfig.lib.json
  • libs/auth/src/session/token.vault.ts
  • libs/plugins/project.json
  • libs/auth/src/session/authorization-vault.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/session/authorization-vault.ts
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Use module augmentation for context properties via `declare module 'frontmcp/sdk'` combined with runtime plugin metadata `contextExtensions`, not module-level side effects

Applied to files:

  • libs/plugins/tsconfig.lib.json
  • libs/plugins/project.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/plugins/tsconfig.lib.json
  • libs/auth/src/session/token.vault.ts
  • libs/plugins/project.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Extend ExecutionContextBase with plugin-specific properties using module declaration (`declare module 'frontmcp/sdk'`) combined with `contextExtensions` in plugin metadata

Applied to files:

  • libs/plugins/tsconfig.lib.json
  • libs/plugins/project.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Extend tool metadata using `declare global` pattern to allow tools to specify plugin-specific options in their decorators

Applied to files:

  • libs/plugins/tsconfig.lib.json
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • libs/plugins/tsconfig.lib.json
  • libs/auth/src/session/token.vault.ts
  • libs/plugins/project.json
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/plugins/tsconfig.lib.json
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/package.json : Entry points must match the documented paths: frontmcp/ui/react, frontmcp/ui/renderers, frontmcp/ui/render, frontmcp/ui/universal, frontmcp/ui/bundler, frontmcp/ui/bridge, frontmcp/ui/components, frontmcp/ui/layouts, frontmcp/ui/web-components

Applied to files:

  • libs/plugins/tsconfig.lib.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/plugins/tsconfig.lib.json
  • libs/auth/src/session/token.vault.ts
  • libs/plugins/project.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/plugins/tsconfig.lib.json
  • libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/plugins/**/*.{ts,tsx} : Use module augmentation and runtime prototype modification to extend ExecutionContextBase with plugin properties

Applied to files:

  • libs/plugins/tsconfig.lib.json
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality

Applied to files:

  • libs/plugins/tsconfig.lib.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/auth/src/session/token.vault.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/auth/src/session/token.vault.ts
  • libs/auth/src/session/authorization-vault.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.

Applied to files:

  • libs/plugins/project.json
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Nx-based monorepo build system - use commands like `nx build sdk`, `nx test ast-guard`, `nx run-many -t test`

Applied to files:

  • libs/plugins/project.json
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Avoid non-null assertions (`!`) - use proper error handling and type guards instead

Applied to files:

  • libs/auth/src/session/authorization-vault.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
libs/plugins/tsconfig.lib.json (1)

13-13: LGTM!

The new path mapping for @frontmcp/plugin-config follows the established pattern and is correctly positioned alphabetically among the other plugin mappings.

libs/plugins/project.json (2)

7-7: LGTM!

The plugin-config implicit dependency is correctly added alongside the existing plugin dependencies. This ensures proper build ordering.


29-29: LGTM! The plugin-config project exists with expected structure.

The @frontmcp/plugin-config external entry is correctly added to both CJS and ESM build configurations (lines 29 and 55), following the established pattern for other plugins. Verification confirms the plugin-config directory has the required project.json, package.json, and src/index.ts entry point.

libs/auth/src/session/token.vault.ts (4)

1-9: LGTM! Proper use of @frontmcp/utils for cryptographic operations.

The imports correctly use @frontmcp/utils for crypto primitives (encryptAesGcm, decryptAesGcm, randomBytes, base64urlEncode, base64urlDecode) instead of node:crypto, aligning with the project's cross-platform cryptographic abstraction. Based on learnings and coding guidelines.


28-50: LGTM! Thorough key validation in constructor.

The constructor properly validates:

  • At least one key is required
  • Each key is a 32-byte Uint8Array (required for AES-256-GCM)
  • No duplicate kid values

The convention of using the first key as active is documented and reasonable for key rotation scenarios.


52-59: LGTM!

Key rotation correctly validates the new key size and allows replacing existing keys (by kid) in the key store, which is appropriate for rotation scenarios.


61-94: LGTM! Correct AES-256-GCM implementation.

The encryption/decryption implementation follows best practices:

  • 12-byte (96-bit) IV for AES-GCM is the standard recommendation
  • Expiration is correctly handled in epoch seconds
  • Unknown kid and expired tokens throw descriptive errors
  • Base64url encoding ensures URL-safe serialization
libs/auth/src/session/authorization-vault.ts (4)

24-25: LGTM! Correct imports.

Uses randomUUID from @frontmcp/utils as required by coding guidelines, and Zod import aligns with the v4 library context.


34-248: LGTM! Well-structured Zod v4 schemas.

The credential type system is comprehensive and correctly uses:

  • z.discriminatedUnion for efficient type discrimination
  • z.literal for discriminator values
  • z.infer for type derivation
  • Appropriate defaults with .default()

945-951: Verify concurrent access requirements for read-modify-write operations.

The Redis implementation uses read-modify-write patterns (e.g., get → modify → set) that aren't atomic. Under concurrent access, this could lead to lost updates. If high concurrency is expected, consider:

  • Redis WATCH/MULTI/EXEC for optimistic locking
  • Lua scripts for atomic operations
  • Document single-writer assumptions

Is this vault expected to handle concurrent modifications to the same entry from multiple processes?


846-874: LGTM! OAuth credential refresh handles both credential types.

The refreshOAuthCredential method correctly handles both oauth and oauth_pkce credential types, properly updates tokens, resets validity state, and maintains the lastAccessAt timestamp.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
libs/sdk/src/auth/session/encrypted-authorization-vault.ts (1)

85-90: Type mismatch: key should be Uint8Array instead of Buffer.

Per the VaultEncryption.deriveKey method (and deriveKeyFromToken), keys are returned as Uint8Array. The EncryptionContext.key type should match to maintain type consistency across the codebase.

🔧 Suggested fix
 export interface EncryptionContext {
   /** Encryption key derived from JWT */
-  key: Buffer;
+  key: Uint8Array;
   /** Vault ID (from JWT jti claim) */
   vaultId: string;
 }
libs/sdk/src/auth/session/record/session.stateful.ts (1)

51-54: Avoid as any cast and use specific error classes.

Two issues in this stub constructor:

  1. ctx as any violates strict TypeScript guidelines—use proper type narrowing or fix the type hierarchy
  2. Per SDK guidelines, use specific error classes with MCP error codes instead of generic Error
🔧 Suggested fix
+import { InternalMcpError } from '../../../errors/mcp.error';
+
   constructor(ctx: StatefulCreateCtx) {
-    super(ctx as any);
-    throw new Error('Method not implemented.');
+    super(ctx as BaseCreateCtx);
+    throw new InternalMcpError('StatefulSession not yet implemented', 'NOT_IMPLEMENTED');
   }

Note: The StatelessSession in the companion file already uses InternalMcpError correctly.

libs/sdk/src/auth/session/token.refresh.ts (1)

111-116: Replace custom base64urlDecode function with import from @frontmcp/utils.

The custom base64urlDecode implementation duplicates functionality already available in @frontmcp/utils. Per the project guidelines, cryptographic and encoding operations should use @frontmcp/utils for consistency and cross-platform support.

♻️ Suggested fix
+import { base64urlDecode } from '@frontmcp/utils';
+
 // ... existing imports ...

-function base64urlDecode(input: string): string {
-  // pad
-  const pad = input.length % 4 === 2 ? '==' : input.length % 4 === 3 ? '=' : '';
-  const s = input.replace(/-/g, '+').replace(/_/g, '/') + pad;
-  return Buffer.from(s, 'base64').toString('utf8');
-}

Remove the local function and import from @frontmcp/utils.

🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts:
- Line 42: The test declares encryptionKey as Uint8Array while the
implementation's EncryptionContext.key is typed as Buffer, risking a TS
mismatch; update EncryptionContext.key's type to Uint8Array (to match
VaultEncryption.deriveKey and the test) and adjust any related uses/overrides to
accept Uint8Array (or a union Buffer | Uint8Array if necessary), ensuring
imports/types referencing EncryptionContext reflect the new type.

In @libs/sdk/src/auth/session/record/session.stateless.ts:
- Line 5: The type name StatefulCreateCtx in this stateless session file is
misleading; rename the type to StatelessCreateCtx (or an equivalent
stateless-sounding name) and update any references in the StatelessSession
implementation—specifically the constructor parameter type for class
StatelessSession—to use the new StatelessCreateCtx name so the type accurately
reflects its usage.

In @libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts:
- Around line 314-327: The extractExpiry method duplicates logic already
implemented in extractCredentialExpiry; remove the duplicate logic in
extractExpiry and instead import and call extractCredentialExpiry from
credential-helpers (ensure the import is added to the top of the file),
returning its result (ensuring the return type remains number | undefined) and
remove any local special-casing; update usages of extractExpiry to continue to
work with the Credential type by forwarding the same Credential parameter into
extractCredentialExpiry.

In @libs/sdk/src/auth/vault/auth-providers.vault.ts:
- Around line 265-278: The extractExpiry method in auth-providers.vault.ts
duplicates logic found in credential-helpers.ts and
auth-providers.accessor.impl.ts; remove the local private
extractExpiry(credential: Credential) implementation and instead import and use
the shared helper from credential-helpers.ts (e.g., the exported extractExpiry
or appropriately named helper function) wherever extractExpiry is called within
the class, updating references to call the imported function; ensure the
Credential type usage remains compatible and adjust imports/exports so there’s a
single source of truth for expiry extraction.

In @libs/sdk/src/common/metadata/tool.metadata.ts:
- Around line 16-25: The TypeScript interface ToolAuthProviderMapping declares
required?: boolean but the runtime schema applies .default(true), so align them
by making the property non-optional (required: boolean) to reflect that after
parsing it will always be true/false; apply the same change to the other
affected interface referenced around lines 32-42 (the similar mapping interface
found there). Alternatively, if you prefer undefined to be a valid runtime
value, remove .default(true) from the corresponding schema instead—choose one
approach and make the interface and schema consistent.
🧹 Nitpick comments (17)
libs/auth/src/session/storage/in-memory-authorization-vault.ts (2)

60-62: Consider handling the connect() promise explicitly.

The fire-and-forget void connect() pattern could silently swallow errors if the adapter's behavior ever changes. For a memory adapter this is low-risk, but the pattern is fragile.

💡 Alternative: use a static factory method
-  constructor(options: InMemoryAuthorizationVaultOptions = {}) {
+  private constructor(
+    memoryAdapter: MemoryStorageAdapter,
+    options: InMemoryAuthorizationVaultOptions = {}
+  ) {
     const memoryAdapter = new MemoryStorageAdapter();
     super(memoryAdapter, {
       namespace: options.namespace ?? 'vault',
       pendingAuthTtlMs: options.pendingAuthTtlMs,
     });
     this.memoryAdapter = memoryAdapter;
-
-    // Connect the adapter (memory adapter connect is instant and synchronous)
-    void this.memoryAdapter.connect();
   }
+
+  static async create(options: InMemoryAuthorizationVaultOptions = {}): Promise<InMemoryAuthorizationVault> {
+    const memoryAdapter = new MemoryStorageAdapter();
+    await memoryAdapter.connect();
+    return new InMemoryAuthorizationVault(memoryAdapter, options);
+  }

Alternatively, if synchronous construction is required, the current approach is acceptable given the documented assumption.


68-73: Sequential deletion could be parallelized.

The for loop with await inside performs deletions sequentially. For testing with small datasets this is fine, but Promise.all() would be more efficient.

💡 Proposed optimization
   async clear(): Promise<void> {
     const keys = await this.memoryAdapter.keys('*');
-    for (const key of keys) {
-      await this.memoryAdapter.delete(key);
-    }
+    await Promise.all(keys.map((key) => this.memoryAdapter.delete(key)));
   }
libs/sdk/src/auth/session/encrypted-authorization-vault.ts (3)

110-116: Consider typing the Redis client more strictly.

The redis: any type violates the strict TypeScript guidelines. Consider defining a minimal interface for the Redis client methods used by this class.

♻️ Suggested interface for Redis client
+/**
+ * Minimal Redis client interface for vault storage
+ */
+interface RedisClient {
+  get(key: string): Promise<string | null>;
+  set(key: string, value: string): Promise<void>;
+  del(key: string): Promise<void>;
+}
+
 export class EncryptedRedisVault implements AuthorizationVault {
   constructor(
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    private readonly redis: any,
+    private readonly redis: RedisClient,
     private readonly encryption: VaultEncryption,
     private readonly namespace = 'vault:',
   ) {}

536-542: Non-null assertion on entry.consent after optional check.

Line 537 uses entry.consent!.selectedToolIds with a non-null assertion. While the code checks entry.consent?.enabled on line 532, the current structure still requires the assertion. Consider restructuring to avoid it.

♻️ Suggested refactor to avoid non-null assertion
   async shouldStoreCredential(vaultId: string, appId: string, toolIds?: string[]): Promise<boolean> {
     const entry = await this.loadEntry(vaultId);
     if (!entry) return false;

-    if (!entry.consent?.enabled) {
+    const consent = entry.consent;
+    if (!consent?.enabled) {
       return true;
     }

     if (toolIds && toolIds.length > 0) {
-      return toolIds.some((toolId) => entry.consent!.selectedToolIds.includes(toolId));
+      return toolIds.some((toolId) => consent.selectedToolIds.includes(toolId));
     }

-    const consentedToolIds = entry.consent.selectedToolIds;
+    const consentedToolIds = consent.selectedToolIds;
     return consentedToolIds.some((toolId) => toolId.startsWith(`${appId}:`));
   }

594-606: Apply same Redis client typing to factory function.

If you add the RedisClient interface as suggested above, update the factory function parameter type as well for consistency.

♻️ Suggested fix
 export function createEncryptedVault(
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  redis: any,
+  redis: RedisClient,
   config: {
     pepper?: string;
     namespace?: string;
   } = {},
 ): { vault: EncryptedRedisVault; encryption: VaultEncryption } {
libs/sdk/src/auth/session/token.store.ts (2)

40-44: Avoid untyped any for the Redis client parameter.

Per coding guidelines, any types require strong justification. The comment acknowledges this is a sketch, but consider defining a minimal interface for the required Redis operations to maintain type safety.

♻️ Suggested interface for Redis client
+/** Minimal Redis client interface for token storage. */
+export interface RedisClientLike {
+  get(key: string): Promise<string | null>;
+  set(key: string, value: string): Promise<unknown>;
+  del(key: string): Promise<unknown>;
+}
+
 /** Redis (sketch) — replace `any` with your redis client type. */
 export class RedisTokenStore implements TokenStore {
   constructor(
-    private readonly redis: any,
+    private readonly redis: RedisClientLike,
     private readonly ns = 'tok:',
   ) {}

58-62: Consider adding runtime validation after JSON.parse.

The type assertion as SecretRecord bypasses runtime checks. If stored data is corrupted or tampered with, this could cause subtle bugs downstream. Consider validating the parsed structure or at minimum checking for required fields.

♻️ Optional: Add basic validation
   async get(id: string) {
     const raw = await this.redis.get(this.key(id));
     if (!raw) return undefined;
-    return JSON.parse(raw) as SecretRecord;
+    const parsed = JSON.parse(raw);
+    if (typeof parsed?.id !== 'string' || !parsed?.blob) {
+      return undefined; // or throw
+    }
+    return parsed as SecretRecord;
   }
libs/sdk/src/auth/session/record/session.stateful.ts (2)

56-58: Use specific MCP error class for unimplemented method.

Per SDK coding guidelines, prefer specific error classes with MCP error codes.

🔧 Suggested fix
   override getToken(providerId?: string): Promise<string> | string {
-    throw new Error('Method not implemented.');
+    throw new InternalMcpError('getToken not yet implemented for StatefulSession', 'NOT_IMPLEMENTED');
   }

60-91: Consider removing or documenting the commented-out code block.

Large commented-out implementations can create maintenance burden. If this is intentional scaffolding for future work, consider adding a brief comment explaining the purpose, or track it via a TODO/issue reference.

libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (2)

104-145: Consider adding deduplication to refresh similar to load.

The refresh method doesn't have deduplication like load does. If multiple callers trigger refresh concurrently for the same provider, this could result in redundant refresh calls and potential race conditions where one refresh overwrites another's result.

♻️ Suggested approach

Consider tracking in-flight refreshes similarly to loads, or documenting that callers are responsible for preventing concurrent refreshes.

 export class LazyCredentialLoader {
   /** In-flight loading promises for deduplication */
   private readonly loading = new Map<string, Promise<ResolvedCredential | null>>();
+  /** In-flight refresh promises for deduplication */
+  private readonly refreshing = new Map<string, Promise<ResolvedCredential | null>>();
   private readonly logger?: FrontMcpLogger;

157-162: cancelAll() clears tracking but doesn't abort in-flight operations.

The method name suggests cancellation, but it only clears the map. In-flight promises will still complete and their results will be lost (not cached). Consider renaming to clearPendingTracking() or documenting this behavior.

📝 Documentation suggestion
   /**
-   * Cancel all in-flight loads (for cleanup)
+   * Clear all in-flight load tracking (for cleanup).
+   * Note: This doesn't abort running operations - they will complete but their results won't be deduplicated.
    */
   cancelAll(): void {
     this.loading.clear();
   }
libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (1)

103-117: Consider wrapping errors instead of mutating them.

Mutating the error object at line 114 is a code smell that can lead to unexpected behavior. Consider wrapping the error in a custom error class instead.

♻️ Suggested approach
+class ProviderLoadError extends Error {
+  constructor(
+    public readonly providerName: string,
+    public readonly cause: unknown,
+  ) {
+    super(`Failed to load provider "${providerName}": ${cause instanceof Error ? cause.message : String(cause)}`);
+    this.name = 'ProviderLoadError';
+  }
+}

   private async loadOne<T extends Credential>(
     config: NormalizedProviderConfig<T>,
     context: CredentialFactoryContext,
   ): Promise<T | null> {
     try {
       return await config.factory({
         ...context,
         metadata: config.metadata,
       });
     } catch (error) {
-      // Add provider name to error for identification
-      (error as { providerName?: string }).providerName = config.name;
-      throw error;
+      throw new ProviderLoadError(config.name, error);
     }
   }

Then update the result processing:

       } else {
-        const providerName = (result.reason as { providerName?: string })?.providerName || 'unknown';
-        const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
+        const reason = result.reason;
+        const providerName = reason instanceof ProviderLoadError ? reason.providerName : 'unknown';
+        const error = reason instanceof Error ? reason : new Error(String(reason));
         failed.set(providerName, error);
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (2)

77-101: Consider preserving provider names for rejected promises in getMany.

When a promise is rejected in getMany, the provider name is lost because it's not captured in the rejection. This makes it impossible to populate the results map with null for failed providers, leaving them absent from the result entirely.

♻️ Suggested approach
   async getMany(
     providerNames: string[],
     options?: GetCredentialOptions,
   ): Promise<Map<string, ResolvedCredential | null>> {
     const results = new Map<string, ResolvedCredential | null>();

     // Load all in parallel
     const promises = providerNames.map(async (name) => {
-      const result = await this.get(name, options);
-      return { name, result };
+      try {
+        const result = await this.get(name, options);
+        return { name, result, error: null };
+      } catch (error) {
+        return { name, result: null, error };
+      }
     });

-    const settled = await Promise.allSettled(promises);
+    const settled = await Promise.all(promises);

-    for (const outcome of settled) {
-      if (outcome.status === 'fulfilled') {
-        results.set(outcome.value.name, outcome.value.result);
-      } else {
-        // Find the provider name from the error context if possible
-        this.logger?.warn('Failed to load credential:', outcome.reason);
-      }
+    for (const { name, result, error } of settled) {
+      results.set(name, result);
+      if (error) {
+        this.logger?.warn(`Failed to load credential "${name}":`, error);
+      }
     }

     return results;
   }

117-126: Consider parallel fetching in headersMany.

The current sequential loop could be slow with many providers. Consider using Promise.all for parallel fetching, similar to getMany.

♻️ Suggested approach
   async headersMany(providerNames: string[]): Promise<Record<string, string>> {
-    const allHeaders: Record<string, string> = {};
-
-    for (const name of providerNames) {
-      const headers = await this.headers(name);
-      Object.assign(allHeaders, headers);
-    }
-
-    return allHeaders;
+    const headerResults = await Promise.all(
+      providerNames.map((name) => this.headers(name)),
+    );
+    return Object.assign({}, ...headerResults);
   }
libs/sdk/src/auth/vault/auth-providers.vault.ts (2)

189-239: Consider returning partial results vs empty array on errors in getAllCredentials.

The empty catch blocks silently swallow errors, which may hide underlying storage issues. Consider logging at debug level or returning a result that indicates partial failure.

📝 Suggested improvement
     // Session scope
     try {
       const sessionCreds = await this.baseVault.getAppCredentials(
         this.buildVaultKey(sessionId, 'session'),
         this.namespace,
       );
       credentials.push(...sessionCreds);
-    } catch {
-      // Ignore
+    } catch (error) {
+      this.logger?.debug('Failed to get session-scoped credentials:', error);
     }

249-263: Consider exhaustive switch handling for CredentialScope.

The default case silently falls back to session scope, which could mask bugs if new scopes are added. Consider using an exhaustive check pattern.

♻️ Suggested approach
   private buildVaultKey(sessionId: string, scope: CredentialScope, userId?: string): string {
     switch (scope) {
       case 'global':
         return `${this.namespace}global`;
       case 'user':
         if (!userId) {
           throw new Error(`userId is required for user-scoped credentials (namespace: ${this.namespace})`);
         }
         return `${this.namespace}user:${userId}`;
       case 'session':
         return `${this.namespace}session:${sessionId}`;
-      default:
-        return `${this.namespace}session:${sessionId}`;
+      default: {
+        // Exhaustive check - this should never happen
+        const _exhaustive: never = scope;
+        throw new Error(`Unknown credential scope: ${_exhaustive}`);
+      }
     }
   }
libs/sdk/src/auth/vault/auth-providers.providers.ts (1)

32-40: Consider using unknown instead of any for factory function types.

Per coding guidelines, prefer unknown over any. The eslint-disable comment acknowledges this but a better approach would be to use unknown with proper type narrowing in consumers.

♻️ Suggested approach
 interface AuthProvidersProviderDef {
   provide: Token;
   scope: ProviderScope;
   name?: string;
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  useFactory?: (...args: any[]) => any;
+  useFactory?: (...args: unknown[]) => unknown;
   useValue?: unknown;
   inject?: () => readonly Token[];
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6aa5015 and b3798f0.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (27)
  • libs/auth/src/index.ts
  • libs/auth/src/session/authorization-vault.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/session/storage/in-memory-authorization-vault.ts
  • libs/auth/src/session/storage/index.ts
  • libs/sdk/src/auth/session/__tests__/authorization-vault.test.ts
  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
  • libs/sdk/src/auth/session/__tests__/vault-encryption.test.ts
  • libs/sdk/src/auth/session/authorization-vault.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/auth/session/token.store.ts
  • libs/sdk/src/auth/session/token.vault.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.ts
  • libs/sdk/src/auth/vault/auth-providers.providers.ts
  • libs/sdk/src/auth/vault/auth-providers.registry.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/vault/credential-cache.ts
  • libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/sdk/src/common/tokens/tool.tokens.ts
💤 Files with no reviewable changes (4)
  • libs/sdk/src/auth/session/tests/vault-encryption.test.ts
  • libs/sdk/src/auth/session/tests/authorization-vault.test.ts
  • libs/sdk/src/auth/session/token.vault.ts
  • libs/sdk/src/auth/session/authorization-vault.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts
  • libs/sdk/src/common/tokens/tool.tokens.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.ts
  • libs/sdk/src/auth/vault/auth-providers.registry.ts
  • libs/sdk/src/auth/vault/credential-cache.ts
  • libs/auth/src/session/storage/index.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/auth/src/session/storage/in-memory-authorization-vault.ts
  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/vault/auth-providers.providers.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/token.store.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
  • libs/auth/src/session/authorization-vault.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/vault/auth-providers.providers.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/token.store.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/vault/auth-providers.providers.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/token.store.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/auth/src/session/storage/in-memory-authorization-vault.ts
  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/vault/auth-providers.providers.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/token.store.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/sdk/src/auth/vault/auth-providers.types.ts
  • libs/auth/src/session/authorization-vault.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Include instanceof checks in tests for error classes to verify proper error hierarchy

Files:

  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/token.store.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Extend tool metadata using `declare global` pattern to allow tools to specify plugin-specific options in their decorators

Applied to files:

  • libs/sdk/src/common/metadata/tool.metadata.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Use module augmentation for context properties via `declare module 'frontmcp/sdk'` combined with runtime plugin metadata `contextExtensions`, not module-level side effects

Applied to files:

  • libs/sdk/src/common/metadata/tool.metadata.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/auth/src/session/authorization-vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files

Applied to files:

  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{validation,**}/**/*.{ts,tsx} : Use `.strict()` on all Zod schemas for validation

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Avoid non-null assertions (`!`) - use proper error handling and type guards instead

Applied to files:

  • libs/auth/src/session/authorization-vault.ts
🧬 Code graph analysis (5)
libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts (1)
libs/auth/src/session/vault-encryption.ts (1)
  • VaultEncryption (102-268)
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (6)
libs/sdk/src/auth/vault/auth-providers.accessor.ts (1)
  • AuthProvidersAccessor (30-159)
libs/sdk/src/auth/vault/auth-providers.registry.ts (2)
  • AuthProvidersRegistry (35-186)
  • NormalizedProviderConfig (21-30)
libs/sdk/src/auth/vault/auth-providers.vault.ts (1)
  • AuthProvidersVault (16-279)
libs/sdk/src/auth/vault/credential-cache.ts (1)
  • CredentialCache (25-242)
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (1)
  • LazyCredentialLoader (17-163)
libs/sdk/src/auth/vault/auth-providers.types.ts (3)
  • CredentialFactoryContext (92-111)
  • GetCredentialOptions (46-53)
  • ResolvedCredential (70-83)
libs/sdk/src/auth/vault/auth-providers.vault.ts (2)
libs/sdk/src/auth/vault/index.ts (3)
  • AuthProvidersVault (81-81)
  • CredentialScope (46-46)
  • AUTH_PROVIDERS_VAULT (81-81)
libs/sdk/src/auth/vault/auth-providers.types.ts (1)
  • CredentialScope (21-21)
libs/sdk/src/auth/vault/auth-providers.providers.ts (7)
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (1)
  • LazyCredentialLoader (17-163)
libs/sdk/src/auth/vault/auth-providers.types.ts (2)
  • AuthProvidersVaultOptions (275-293)
  • CredentialFactoryContext (92-111)
libs/sdk/src/auth/vault/auth-providers.registry.ts (2)
  • AUTH_PROVIDERS_REGISTRY (191-191)
  • AuthProvidersRegistry (35-186)
libs/sdk/src/auth/vault/auth-providers.vault.ts (2)
  • AUTH_PROVIDERS_VAULT (284-284)
  • AuthProvidersVault (16-279)
libs/sdk/src/auth/vault/credential-cache.ts (2)
  • CREDENTIAL_CACHE (247-247)
  • CredentialCache (25-242)
libs/sdk/src/auth/vault/auth-providers.accessor.ts (1)
  • AUTH_PROVIDERS_ACCESSOR (169-169)
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (1)
  • AuthProvidersAccessorImpl (20-328)
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (3)
libs/sdk/src/auth/vault/auth-providers.types.ts (2)
  • ResolvedCredential (70-83)
  • CredentialFactoryContext (92-111)
libs/sdk/src/auth/vault/auth-providers.registry.ts (1)
  • NormalizedProviderConfig (21-30)
libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts (1)
  • extractCredentialExpiry (15-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (39)
libs/auth/src/session/storage/in-memory-authorization-vault.ts (2)

1-21: LGTM!

Good documentation explaining the development/testing purpose with clear guidance to use persistent adapters in production. Import from @frontmcp/utils follows the project conventions.


23-38: LGTM!

Interface is well-documented with sensible defaults for a testing-focused implementation.

libs/auth/src/session/authorization-vault.ts (4)

24-26: LGTM - Proper use of @frontmcp/utils for UUID generation.

Correctly imports randomUUID from @frontmcp/utils instead of using node:crypto directly, in compliance with the coding guidelines.


34-47: Well-structured credential type enumeration.

The credential type schema provides a comprehensive set of authentication methods with clear documentation. The enum values are self-descriptive and cover common auth patterns.


224-247: Good use of Zod discriminated union for type-safe credential handling.

The discriminated union on the type field enables TypeScript to narrow credential types correctly. The type exports align well with the schemas.


386-534: Comprehensive AuthorizationVault interface with clear contracts.

The interface defines a complete API surface for vault operations including CRUD, consent management, credential lifecycle, and OAuth token refresh. Method signatures are well-typed and documented.

libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts (3)

51-56: LGTM - Properly updated to async key derivation.

The beforeEach hook is correctly made async to accommodate the now-async deriveKey method, ensuring the encryption key is properly derived before tests run.


179-210: Good test for decryption failure with wrong key.

This test properly validates the security property that decryption fails when using an incorrect key, ensuring encrypted data cannot be accessed without the proper credentials.


600-647: Excellent zero-knowledge property validation.

This test comprehensively verifies that:

  1. Metadata remains accessible without decryption
  2. Sensitive data is encrypted and unreadable
  3. Decryption fails with incorrect keys

This is crucial for validating the security guarantees of the vault.

libs/sdk/src/auth/session/encrypted-authorization-vault.ts (4)

29-41: LGTM - Imports consolidated from @frontmcp/auth.

The imports are properly sourced from the new public auth package, aligning with the module refactor objectives.


169-178: LGTM - Encryption methods properly converted to async.

The encryptSensitive and decryptSensitive methods are correctly updated to return Promises, matching the async nature of the underlying VaultEncryption methods.


183-227: LGTM - Entry transformation methods properly handle async encryption.

Both toVaultEntry and toRedisEntry correctly await the encryption/decryption operations before returning their results.


232-251: LGTM - Save and load operations properly await async transformations.

The saveEntry and loadEntry methods correctly await the async toRedisEntry and toVaultEntry calls respectively.

libs/sdk/src/auth/session/token.store.ts (1)

3-3: LGTM!

Import path correctly updated to use @frontmcp/auth for the EncBlob type, aligning with the broader refactor centralizing auth types in the external package.

libs/sdk/src/common/metadata/tool.metadata.ts (2)

263-305: Well-documented authProviders API with comprehensive examples.

The JSDoc examples clearly demonstrate all supported usage patterns (shorthand, full mapping, multiple providers). The schema integration at line 349 properly validates against toolAuthProviderMappingSchema.

Also applies to: 349-349


176-181: Pre-existing any usage in StructuredOutputType.

Lines 178 and 180-181 use z.ZodObject<any>. Per coding guidelines, any types require justification. This appears to be pre-existing code, but if touched in this PR, consider using z.ZodObject<z.ZodRawShape> or a more constrained type.

#!/bin/bash
# Check if these lines were modified in this PR or are pre-existing
git log --oneline -1 -- libs/sdk/src/common/metadata/tool.metadata.ts
git diff HEAD~1 -- libs/sdk/src/common/metadata/tool.metadata.ts | grep -A2 -B2 "ZodObject<any>"
libs/sdk/src/auth/session/record/session.stateless.ts (1)

2-2: LGTM!

Import correctly updated to use @frontmcp/auth for TokenVault, consistent with the auth module refactoring.

libs/sdk/src/auth/session/record/session.stateful.ts (1)

4-4: LGTM!

Import correctly updated to use @frontmcp/auth for the TokenVault type.

libs/sdk/src/auth/session/token.refresh.ts (1)

4-4: LGTM!

Import correctly updated to source TokenVault from @frontmcp/auth, consistent with the auth module consolidation.

libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (3)

1-13: LGTM on imports and module structure.

The lazy credential loader is well-structured with proper type imports from @frontmcp/auth and internal modules. The use of extractCredentialExpiry from credential-helpers promotes code reuse.


17-24: LGTM on class setup and logger initialization.

The class properly uses a child logger for scoped logging and maintains the in-flight map as a readonly private member.


34-54: LGTM on the deduplication pattern.

The in-flight deduplication logic correctly prevents concurrent loads for the same provider. The finally block ensures proper cleanup even on failure.

libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (2)

15-25: LGTM on the EagerLoadResult interface.

The interface clearly documents the result structure with loaded credentials, failed providers, and timing information.


48-98: LGTM on parallel loading with Promise.allSettled.

The implementation correctly uses Promise.allSettled to handle both successful and failed loads without short-circuiting, and properly categorizes results including required-null as failures.

libs/sdk/src/auth/vault/auth-providers.types.ts (5)

1-9: LGTM on module structure and imports.

Clean separation of type definitions with proper Zod imports for runtime validation.


55-61: Good use of .strict() on Zod schemas.

The schema correctly uses .strict() which aligns with validation best practices. Based on learnings, this is the expected pattern for Zod schemas in this codebase.


222-232: LGTM on the union schema with default.

The authProviderMappingSchema correctly uses Zod 4's union pattern with .default(true) for the required field, allowing both string shorthand and object configurations.


295-304: LGTM on the vault options schema.

The schema properly uses .optional().default() pattern from Zod 4 for sensible defaults while maintaining strict validation.


187-201: The original comment should be removed. The suggestion to use z.function() is incorrect for Zod v4. In Zod v4, z.function() is a function factory for creating validated functions with .implement() or .implementAsync(), not a schema validator for checking if a value is a function. The current implementation using z.any() is the correct approach for validating that these properties are functions at runtime, and the existing code comment accurately explains this Zod 4 compatibility decision.

Likely an incorrect or invalid review comment.

libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (2)

1-16: LGTM on imports and class structure.

The implementation properly imports all required types and follows the interface contract from AuthProvidersAccessor.


34-75: LGTM on the credential retrieval flow.

The get method correctly implements the cache → vault → factory fallback pattern with proper cache population at each level.

libs/sdk/src/auth/vault/auth-providers.vault.ts (3)

1-12: LGTM on module structure and namespace isolation approach.

The vault correctly wraps the base AuthorizationVault with a dedicated namespace to prevent key collisions.


36-61: LGTM on credential storage with proper error handling.

The storeCredential method correctly builds the vault key, constructs the AppCredential shape, and re-throws errors after logging for proper error propagation.


72-103: LGTM on credential retrieval with validity checks.

The getCredential method properly validates isValid flag and expiry time before returning credentials, returning null on failure to avoid propagating vault errors.

libs/sdk/src/auth/vault/auth-providers.providers.ts (5)

1-22: LGTM on imports and module structure.

The DI provider module correctly imports all necessary tokens and types for wiring the AuthProviders system.


55-71: LGTM on GLOBAL scope providers.

Registry and Vault are correctly scoped as singletons at GLOBAL level.


73-88: LGTM on CONTEXT scope providers.

CredentialCache and LazyCredentialLoader are correctly scoped per-request/session at CONTEXT level, ensuring isolation between requests.


90-125: LGTM on AuthProvidersAccessor factory wiring.

The factory correctly builds the CredentialFactoryContext from FrontMcpContext and properly injects all dependencies. The comment at lines 109-110 clarifies that appId and toolId are set by tool flows.


130-144: LGTM on the enable check logic.

The isAuthProvidersEnabled function correctly implements the enable logic: explicit false → disabled, explicit true → enabled, otherwise auto-enable based on registered providers.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @libs/auth/project.json:
- Line 6: Add an API reference MDX documenting the public exports of the new
@frontmcp/auth package: create docs/draft/docs/authentication/api-reference.mdx
(matching other libs) and include sections for JwksService, the authorization
vault, exported credential types, session management functions/classes, and
available UI templates with short usage examples and import paths; ensure the
MDX mirrors the structure and metadata style used by existing api-reference
files so it’s picked up by the docs pipeline and references the exact exported
symbols from @frontmcp/auth.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3798f0 and 2896832.

📒 Files selected for processing (22)
  • apps/e2e/demo-e2e-agents/jest.e2e.config.ts
  • apps/e2e/demo-e2e-cache/jest.e2e.config.ts
  • apps/e2e/demo-e2e-codecall/jest.e2e.config.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-direct/jest.e2e.config.ts
  • apps/e2e/demo-e2e-errors/jest.e2e.config.ts
  • apps/e2e/demo-e2e-hooks/jest.e2e.config.ts
  • apps/e2e/demo-e2e-multiapp/jest.e2e.config.ts
  • apps/e2e/demo-e2e-notifications/jest.e2e.config.ts
  • apps/e2e/demo-e2e-openapi/jest.e2e.config.ts
  • apps/e2e/demo-e2e-providers/jest.e2e.config.ts
  • apps/e2e/demo-e2e-public/jest.e2e.config.ts
  • apps/e2e/demo-e2e-redis/jest.e2e.config.ts
  • apps/e2e/demo-e2e-remember/jest.e2e.config.ts
  • apps/e2e/demo-e2e-serverless/jest.e2e.config.ts
  • apps/e2e/demo-e2e-standalone/jest.e2e.config.ts
  • apps/e2e/demo-e2e-transparent/jest.e2e.config.ts
  • apps/e2e/demo-e2e-transport-recreation/jest.e2e.config.ts
  • apps/e2e/demo-e2e-ui/jest.e2e.config.ts
  • libs/auth/project.json
  • libs/auth/tsconfig.lib.json
  • libs/sdk/tsconfig.lib.json
💤 Files with no reviewable changes (19)
  • apps/e2e/demo-e2e-ui/jest.e2e.config.ts
  • apps/e2e/demo-e2e-redis/jest.e2e.config.ts
  • apps/e2e/demo-e2e-serverless/jest.e2e.config.ts
  • apps/e2e/demo-e2e-public/jest.e2e.config.ts
  • apps/e2e/demo-e2e-hooks/jest.e2e.config.ts
  • apps/e2e/demo-e2e-transparent/jest.e2e.config.ts
  • apps/e2e/demo-e2e-direct/jest.e2e.config.ts
  • apps/e2e/demo-e2e-providers/jest.e2e.config.ts
  • apps/e2e/demo-e2e-openapi/jest.e2e.config.ts
  • apps/e2e/demo-e2e-errors/jest.e2e.config.ts
  • apps/e2e/demo-e2e-standalone/jest.e2e.config.ts
  • apps/e2e/demo-e2e-cache/jest.e2e.config.ts
  • apps/e2e/demo-e2e-agents/jest.e2e.config.ts
  • apps/e2e/demo-e2e-notifications/jest.e2e.config.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-remember/jest.e2e.config.ts
  • apps/e2e/demo-e2e-multiapp/jest.e2e.config.ts
  • apps/e2e/demo-e2e-codecall/jest.e2e.config.ts
  • apps/e2e/demo-e2e-transport-recreation/jest.e2e.config.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/auth/tsconfig.lib.json
  • libs/sdk/tsconfig.lib.json
🧰 Additional context used
📓 Path-based instructions (1)
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/auth/project.json
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Nx-based monorepo build system - use commands like `nx build sdk`, `nx test ast-guard`, `nx run-many -t test`

Applied to files:

  • libs/auth/project.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
libs/auth/project.json (4)

1-6: Well-structured project metadata for the new auth library.

The project configuration correctly identifies this as a publishable library with synchronized versioning. The tags align with the monorepo conventions.


8-55: Build configuration follows best practices for dual CJS/ESM output.

The build pipeline is well-structured:

  • CJS build generates declarations and runs type-checking.
  • ESM build correctly depends on CJS, skips redundant type-checking, and uses .mjs extension.
  • External dependencies are consistent between both formats.

56-63: Test configuration looks good.

The Jest configuration is standard. Note that passWithNoTests: true is appropriate for initial setup but consider removing it once tests are established to catch accidental test file deletion.


49-78: No issues found. All referenced scripts exist and are properly configured: scripts/strip-dist-from-pkg.js and scripts/publish-alpha.sh are both present and executable.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @libs/auth/src/session/vault-encryption.ts:
- Around line 194-196: Replace the Node-only encodings for iv, ciphertext, and
tag with the cross-platform helper: import base64urlEncode from
'@frontmcp/utils' (or the named export used elsewhere) and change iv:
Buffer.from(iv).toString('base64'), ct:
Buffer.from(ciphertext).toString('base64'), tag:
Buffer.from(tag).toString('base64') to iv: base64urlEncode(iv), ct:
base64urlEncode(ciphertext), tag: base64urlEncode(tag); ensure the import is
added to this module so the same RFC4648 base64url encoding used in
libs/utils/src/crypto/encrypted-blob.ts is applied here.
🧹 Nitpick comments (24)
libs/utils/src/storage/__tests__/typed-storage.test.ts (4)

72-82: Consider adding a test for TTL expiration behavior.

The TTL tests verify that TTL is set correctly, but there's no test verifying that values become inaccessible after TTL expires. This would require either mocking time or using a very short TTL with a small delay.

💡 Example test for TTL expiration
it('should return null after TTL expires', async () => {
  jest.useFakeTimers();
  const user: TestUser = { id: '1', name: 'Alice' };
  await storage.set('user:1', user, { ttlSeconds: 1 });

  // Fast-forward time past TTL
  jest.advanceTimersByTime(1500);

  const result = await storage.get('user:1');
  expect(result).toBeNull();
  jest.useRealTimers();
});

158-175: Consider adding a test for partial mdelete.

Testing mdelete with a mix of existing and non-existing keys would verify correct count behavior in edge cases.

💡 Example test
it('should return correct count when some keys do not exist', async () => {
  const user: TestUser = { id: '1', name: 'Alice' };
  await storage.set('user:1', user);

  const deleted = await storage.mdelete(['user:1', 'user:non-existent']);
  expect(deleted).toBe(1);
});

255-265: Add instanceof check for the thrown error.

Per coding guidelines, error tests should include instanceof checks to verify proper error hierarchy. This helps ensure the correct error type is thrown.

♻️ Proposed enhancement
     it('should throw for invalid data when throwOnInvalid is true', async () => {
       const validatedStorage = new TypedStorage<TestUser>(adapter, {
         schema: testUserSchema,
         throwOnInvalid: true,
       });

       // Store invalid data directly
       await adapter.set('user:invalid', JSON.stringify({ invalid: 'data' }));

-      await expect(validatedStorage.get('user:invalid')).rejects.toThrow();
+      await expect(validatedStorage.get('user:invalid')).rejects.toThrow();
+      
+      // Verify error type
+      try {
+        await validatedStorage.get('user:invalid');
+      } catch (error) {
+        expect(error).toBeInstanceOf(z.ZodError);
+      }
     });

310-318: Add instanceof check for the JSON parse error.

Similar to the schema validation test, this error test should verify the error type to ensure consistent error handling behavior.

♻️ Proposed enhancement
     it('should throw for invalid JSON when throwOnInvalid is true', async () => {
       const throwingStorage = new TypedStorage<TestUser>(adapter, {
         throwOnInvalid: true,
       });

       await adapter.set('invalid:json', 'not valid json');

-      await expect(throwingStorage.get('invalid:json')).rejects.toThrow();
+      await expect(throwingStorage.get('invalid:json')).rejects.toThrow(SyntaxError);
     });
libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts (4)

62-68: Add error type verification alongside message check.

Per coding guidelines, include instanceof checks in tests for error classes to verify proper error hierarchy. This test only verifies the error message but not the error type.

Suggested improvement
     it('should throw if key is not 32 bytes', () => {
-      expect(() => {
-        new EncryptedTypedStorage<TestSecret>(adapter, {
-          keys: [{ kid: 'short', key: randomBytes(16) }],
-        });
-      }).toThrow('must be 32 bytes');
+      const createWithShortKey = () => {
+        new EncryptedTypedStorage<TestSecret>(adapter, {
+          keys: [{ kid: 'short', key: randomBytes(16) }],
+        });
+      };
+      expect(createWithShortKey).toThrow(EncryptedStorageError);
+      expect(createWithShortKey).toThrow('must be 32 bytes');
     });

297-303: Add error type verification for consistency.

Similar to the constructor test, this should verify both the error type and message for complete error hierarchy validation.

Suggested improvement
     it('should throw when rotating to invalid key', () => {
       const storage = new EncryptedTypedStorage<TestSecret>(adapter, { keys: [key1] });
 
-      expect(() => {
-        storage.rotateKey({ kid: 'bad', key: randomBytes(16) });
-      }).toThrow('must be 32 bytes');
+      const rotateToInvalidKey = () => {
+        storage.rotateKey({ kid: 'bad', key: randomBytes(16) });
+      };
+      expect(rotateToInvalidKey).toThrow(EncryptedStorageError);
+      expect(rotateToInvalidKey).toThrow('must be 32 bytes');
     });

336-351: Consider verifying error type in addition to message.

For consistency with other error tests and per coding guidelines about instanceof checks, consider also verifying the error class.

Suggested improvement
-      await expect(storage.get('secret:invalid')).rejects.toThrow('Schema validation failed');
+      await expect(storage.get('secret:invalid')).rejects.toThrow(EncryptedStorageError);
+      await expect(storage.get('secret:invalid')).rejects.toThrow('Schema validation failed');

Note: The duplicate await expect can be avoided by capturing the promise in a variable or using a try/catch block if preferred.


367-403: Add error type assertions for throwOnError=true tests.

The error handling tests at lines 375 and 402 only verify error messages. Adding error type verification would ensure proper error hierarchy per coding guidelines.

Suggested improvement for lines 374-375
-      await expect(storage.get('secret:corrupted')).rejects.toThrow('Failed to parse');
+      await expect(storage.get('secret:corrupted')).rejects.toThrow(EncryptedStorageError);
Suggested improvement for lines 401-402
-      await expect(storage.get('secret:unknown')).rejects.toThrow('Unknown encryption key');
+      await expect(storage.get('secret:unknown')).rejects.toThrow(EncryptedStorageError);
libs/auth/src/session/storage/storage-token-store.ts (1)

104-107: Minor: Redundant nullish coalescing with undefined.

record ?? undefined is equivalent to just record when record is already T | undefined. The storage's get method already returns undefined for missing keys.

Suggested simplification
   async get(id: string): Promise<SecretRecord | undefined> {
     const record = await this.storage.get(this.key(id));
-    return record ?? undefined;
+    return record;
   }
libs/auth/src/jwks/jwks.service.ts (2)

58-62: Consider extracting environment check to a testable utility.

The shouldEnablePersistence method directly reads process.env['NODE_ENV'], making it harder to unit test different environment scenarios. Consider accepting this as a constructor option or using a utility that can be mocked.


68-76: Potential issue: hardcoded persistence directory.

The baseDir: '.frontmcp/keys' is hardcoded. Consider making this configurable via JwksServiceOptions to allow flexibility in different deployment scenarios.

Suggested enhancement
+  // In JwksServiceOptions type:
+  /** Base directory for key persistence (development mode). Default: '.frontmcp/keys' */
+  persistenceDir?: string;

   private async getKeyPersistence(): Promise<KeyPersistence | null> {
     if (!this.shouldEnablePersistence()) return null;
     if (!this.keyPersistence) {
       this.keyPersistence = await createKeyPersistence({
-        baseDir: '.frontmcp/keys',
+        baseDir: this.opts.persistenceDir ?? '.frontmcp/keys',
       });
     }
     return this.keyPersistence;
   }
libs/auth/src/jwks/__tests__/jwks.service.advanced.test.ts (3)

248-259: Test relies on external service - consider mocking.

This test depends on httpstat.us, an external service. This makes the test flaky and dependent on network availability. Consider mocking the fetch call or using a local server setup for more reliable CI/CD execution.

Alternative approach using fetch mock
it('should use custom network timeout', async () => {
  const service = new JwksService({ networkTimeoutMs: 1 });
  
  // Mock fetch to simulate slow response
  const originalFetch = global.fetch;
  global.fetch = jest.fn().mockImplementation(() => 
    new Promise((resolve) => setTimeout(resolve, 5000))
  );
  
  try {
    const result = await service.getJwksForProvider({
      id: 'slow-provider',
      issuerUrl: 'https://example.com',
      jwksUri: 'https://example.com/.well-known/jwks.json',
    });
    expect(result).toBeUndefined();
  } finally {
    global.fetch = originalFetch;
  }
});

261-278: TTL test may be flaky due to timing sensitivity.

The test uses a 1ms TTL and 10ms delay, which can be unreliable across different environments. Consider using Jest's fake timers for deterministic behavior.

Using fake timers
it('should use custom provider JWKS TTL', async () => {
  jest.useFakeTimers();
  const service = new JwksService({ providerJwksTtlMs: 1 });

  const jwks1 = { keys: [{ kty: 'RSA', kid: 'v1', alg: 'RS256', use: 'sig' }] };
  service.setProviderJwks('provider1', jwks1 as any);

  // Advance time past TTL
  jest.advanceTimersByTime(10);

  const result = await service.getJwksForProvider({
    id: 'provider1',
    issuerUrl: 'https://example.com',
  });

  expect(result).toEqual(jwks1);
  jest.useRealTimers();
});

63-71: Consider adding verification that cached JWKS has correct structure.

The test sets a minimal JWKS with as any. While acceptable for testing caching behavior, consider also testing that the cached result maintains proper structure, not just equality.

libs/auth/src/session/vault-encryption.ts (2)

125-137: Inefficient pepper handling - unnecessary encode/decode cycle.

The pepper is stored as Uint8Array but then decoded back to string in deriveKey. This round-trip is unnecessary - you could concatenate the bytes directly.

More efficient approach
   async deriveKey(claims: VaultKeyDerivationClaims): Promise<Uint8Array> {
-    const ikmParts = [
-      claims.jti,
-      claims.vaultKey ?? '',
-      claims.sub,
-      claims.iat.toString(),
-      new TextDecoder().decode(this.pepper),
-    ];
-    const ikm = new TextEncoder().encode(ikmParts.join(''));
+    const encoder = new TextEncoder();
+    const claimsPart = encoder.encode(
+      [claims.jti, claims.vaultKey ?? '', claims.sub, claims.iat.toString()].join('')
+    );
+    // Concatenate claims bytes with pepper bytes directly
+    const ikm = new Uint8Array(claimsPart.length + this.pepper.length);
+    ikm.set(claimsPart, 0);
+    ikm.set(this.pepper, claimsPart.length);

     const infoBytes = new TextEncoder().encode(this.hkdfInfo);
     const key = await hkdfSha256(ikm, new Uint8Array(0), infoBytes, 32);
     return key;
   }

157-170: Same inefficiency in deriveKeyFromToken.

This method has the same pepper decode/encode cycle. Apply the same optimization for consistency.

libs/auth/src/ui/templates.ts (3)

293-302: Consider accessibility: Ensure checkbox state is programmatically accessible.

The "Select all" checkbox uses inline JavaScript that modifies other checkboxes. For screen reader users, consider adding aria-controls to indicate which elements are controlled, or use a more semantic pattern.


407-416: Inline script injection risk mitigated but consider CSP compatibility.

The inline <script> block works for runtime rendering but may conflict with strict Content Security Policy (CSP) headers that disallow inline scripts. If CSP is enforced, consider using nonce attributes or moving logic to an external script.


557-559: renderToHtml appears unused - consider removing or documenting purpose.

The function just returns the input unchanged. If it's for API compatibility, add documentation explaining the legacy pattern it supports.

Add clarifying documentation
 /**
  * Simple wrapper for compatibility - just returns the HTML string
  * (Templates are already complete HTML documents)
+ *
+ * @deprecated This function is a no-op kept for backward compatibility.
+ * Template builders already return complete HTML documents.
  */
 export function renderToHtml(html: string, _options?: { title?: string }): string {
   return html;
 }
libs/auth/src/session/authorization-vault.ts (2)

24-26: Remove or update the dangling comment about randomUUID.

Line 25 contains a comment suggesting randomUUID can be imported from @frontmcp/utils, but this file defines only types and schemas with no runtime code requiring UUID generation. Consider removing this comment to avoid confusion, or relocate it to an implementation file where it's applicable.

Suggested fix
 import { z } from 'zod';
-// randomUUID can be imported from @frontmcp/utils if needed
 
 // ============================================

320-339: Consider documenting the TTL behavior for pending incremental auth.

The pendingIncrementalAuthSchema includes createdAt and expiresAt fields, but the relationship between them (e.g., default TTL) is only documented in implementation classes. Adding a brief JSDoc note about expected TTL handling would improve API clarity for consumers.

libs/utils/src/index.ts (1)

114-130: Consider adding @deprecated JSDoc tags to legacy secret persistence exports.

The comment "(legacy)" on line 114 is helpful, but consumers using IDE autocompletion won't see the deprecation warning. Adding @deprecated annotations in the source files (./crypto) would provide better DX by showing warnings at usage sites.

libs/auth/src/session/storage/storage-authorization-vault.ts (2)

120-133: Silent failure on missing vault entry may hide bugs.

The update() method logs a warning and silently returns when the vault entry doesn't exist (lines 122-125). While this prevents errors, it may mask issues where callers expect the entry to exist. Consider:

  1. Documenting this behavior in the interface JSDoc, or
  2. Throwing an error for callers who need to handle missing entries explicitly

The same pattern appears in updateConsent, authorizeApp, completePendingAuth, cancelPendingAuth, and credential methods.


292-306: Consider logging when credential storage is skipped due to consent.

In addAppCredential(), when shouldStoreCredential returns false (lines 297-300), the method silently returns. This could be confusing when debugging why credentials aren't being stored. A debug-level log would aid troubleshooting.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2896832 and 9260c23.

📒 Files selected for processing (24)
  • libs/auth/package.json
  • libs/auth/src/index.ts
  • libs/auth/src/jwks/__tests__/jwks.service.advanced.test.ts
  • libs/auth/src/jwks/jwks.service.ts
  • libs/auth/src/jwks/jwks.types.ts
  • libs/auth/src/session/authorization-vault.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/session/storage/in-memory-authorization-vault.ts
  • libs/auth/src/session/storage/index.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
  • libs/auth/src/session/storage/storage-token-store.ts
  • libs/auth/src/session/token.store.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/auth/src/ui/__tests__/base-layout.advanced.test.ts
  • libs/auth/src/ui/templates.ts
  • libs/utils/src/index.ts
  • libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts
  • libs/utils/src/storage/__tests__/typed-storage.test.ts
  • libs/utils/src/storage/encrypted-typed-storage.ts
  • libs/utils/src/storage/encrypted-typed-storage.types.ts
  • libs/utils/src/storage/errors.ts
  • libs/utils/src/storage/index.ts
  • libs/utils/src/storage/typed-storage.ts
  • libs/utils/src/storage/typed-storage.types.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • libs/auth/src/ui/tests/base-layout.advanced.test.ts
  • libs/auth/src/session/storage/in-memory-authorization-vault.ts
  • libs/auth/package.json
  • libs/auth/src/session/storage/index.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/auth/src/jwks/jwks.types.ts
  • libs/auth/src/session/storage/storage-token-store.ts
  • libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts
  • libs/auth/src/jwks/jwks.service.ts
  • libs/utils/src/storage/__tests__/typed-storage.test.ts
  • libs/auth/src/session/token.store.ts
  • libs/auth/src/jwks/__tests__/jwks.service.advanced.test.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/utils/src/index.ts
  • libs/auth/src/ui/templates.ts
  • libs/auth/src/session/authorization-vault.ts
  • libs/auth/src/index.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/auth/src/jwks/jwks.types.ts
  • libs/auth/src/session/storage/storage-token-store.ts
  • libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts
  • libs/auth/src/jwks/jwks.service.ts
  • libs/utils/src/storage/__tests__/typed-storage.test.ts
  • libs/auth/src/session/token.store.ts
  • libs/auth/src/jwks/__tests__/jwks.service.advanced.test.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/utils/src/index.ts
  • libs/auth/src/ui/templates.ts
  • libs/auth/src/session/authorization-vault.ts
  • libs/auth/src/index.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Include instanceof checks in tests for error classes to verify proper error hierarchy

Files:

  • libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts
  • libs/utils/src/storage/__tests__/typed-storage.test.ts
  • libs/auth/src/jwks/__tests__/jwks.service.advanced.test.ts
libs/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Files:

  • libs/utils/src/index.ts
  • libs/auth/src/index.ts
🧠 Learnings (26)
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx} : Do not use `any` type without justification in TypeScript code

Applied to files:

  • libs/auth/src/jwks/jwks.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files

Applied to files:

  • libs/auth/src/jwks/jwks.types.ts
  • libs/auth/src/session/token.store.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use strict TypeScript mode with no `any` types without strong justification

Applied to files:

  • libs/auth/src/jwks/jwks.types.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `any` type without justification in TypeScript files

Applied to files:

  • libs/auth/src/jwks/jwks.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Use type parameters with constraints instead of unconstrained generics with `any` defaults

Applied to files:

  • libs/auth/src/jwks/jwks.types.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/utils/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.test.ts : Tests must achieve 95%+ code coverage across all metrics; use `MockStore` implementing `RememberStoreInterface` for RememberPlugin testing

Applied to files:

  • libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts
  • libs/utils/src/storage/__tests__/typed-storage.test.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/auth/src/jwks/jwks.service.ts
  • libs/auth/src/session/authorization-vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/auth/src/jwks/jwks.service.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/utils/src/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/auth/src/jwks/jwks.service.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/utils/src/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/auth/src/jwks/jwks.service.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/utils/src/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)

Applied to files:

  • libs/utils/src/storage/__tests__/typed-storage.test.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : Use React Testing Library for component tests and include SSR/hydration tests for all interactive components

Applied to files:

  • libs/utils/src/storage/__tests__/typed-storage.test.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rate

Applied to files:

  • libs/utils/src/storage/__tests__/typed-storage.test.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Applied to files:

  • libs/auth/src/session/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/auth/src/session/index.ts
  • libs/auth/src/ui/templates.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,build}/**/*.{ts,tsx} : Use htmlRenderer for HTML template rendering without React dependency

Applied to files:

  • libs/auth/src/ui/templates.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,base-template}/**/*.{ts,tsx,js,jsx} : Never skip HTML escaping for user-provided content in renderers and templates

Applied to files:

  • libs/auth/src/ui/templates.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,utils}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test HTML escaping for user-provided content to prevent XSS vulnerabilities

Applied to files:

  • libs/auth/src/ui/templates.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Avoid non-null assertions (`!`) - use proper error handling and type guards instead

Applied to files:

  • libs/auth/src/session/authorization-vault.ts
  • libs/auth/src/session/storage/storage-authorization-vault.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality

Applied to files:

  • libs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx,js,jsx} : Do not expose internal error details in public APIs - use sanitized error messages

Applied to files:

  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/auth/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • libs/auth/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/universal/**/*.{ts,tsx} : Universal app shell (UniversalApp, FrontMCPProvider) must provide platform-agnostic React context and initialization

Applied to files:

  • libs/auth/src/index.ts
🧬 Code graph analysis (5)
libs/utils/src/storage/__tests__/typed-storage.test.ts (1)
libs/utils/src/storage/typed-storage.ts (5)
  • raw (193-195)
  • ttl (165-167)
  • exists (95-97)
  • keys (175-177)
  • count (185-187)
libs/auth/src/session/token.store.ts (1)
libs/auth/src/session/token.vault.ts (1)
  • EncBlob (10-18)
libs/auth/src/jwks/__tests__/jwks.service.advanced.test.ts (1)
libs/auth/src/jwks/jwks.service.ts (1)
  • JwksService (15-506)
libs/auth/src/ui/templates.ts (1)
libs/auth/src/ui/base-layout.ts (4)
  • escapeHtml (157-157)
  • wideLayout (357-366)
  • centeredCardLayout (340-352)
  • extraWideLayout (371-380)
libs/auth/src/session/authorization-vault.ts (3)
libs/auth/src/index.ts (29)
  • credentialTypeSchema (88-88)
  • CredentialType (127-127)
  • oauthCredentialSchema (89-89)
  • apiKeyCredentialSchema (90-90)
  • basicAuthCredentialSchema (91-91)
  • bearerCredentialSchema (92-92)
  • privateKeyCredentialSchema (93-93)
  • mtlsCredentialSchema (94-94)
  • customCredentialSchema (95-95)
  • sshKeyCredentialSchema (96-96)
  • serviceAccountCredentialSchema (97-97)
  • pkceOAuthCredentialSchema (98-98)
  • credentialSchema (99-99)
  • OAuthCredential (128-128)
  • ApiKeyCredential (129-129)
  • BasicAuthCredential (130-130)
  • BearerCredential (131-131)
  • PrivateKeyCredential (132-132)
  • MtlsCredential (133-133)
  • CustomCredential (134-134)
  • SshKeyCredential (135-135)
  • ServiceAccountCredential (136-136)
  • PkceOAuthCredential (137-137)
  • Credential (138-138)
  • AppCredential (139-139)
  • VaultConsentRecord (140-140)
  • VaultFederatedRecord (141-141)
  • PendingIncrementalAuth (142-142)
  • AuthorizationVault (144-144)
libs/auth/src/session/index.ts (29)
  • credentialTypeSchema (10-10)
  • CredentialType (30-30)
  • oauthCredentialSchema (11-11)
  • apiKeyCredentialSchema (12-12)
  • basicAuthCredentialSchema (13-13)
  • bearerCredentialSchema (14-14)
  • privateKeyCredentialSchema (15-15)
  • mtlsCredentialSchema (16-16)
  • customCredentialSchema (17-17)
  • sshKeyCredentialSchema (18-18)
  • serviceAccountCredentialSchema (19-19)
  • pkceOAuthCredentialSchema (20-20)
  • credentialSchema (21-21)
  • OAuthCredential (31-31)
  • ApiKeyCredential (32-32)
  • BasicAuthCredential (33-33)
  • BearerCredential (34-34)
  • PrivateKeyCredential (35-35)
  • MtlsCredential (36-36)
  • CustomCredential (37-37)
  • SshKeyCredential (38-38)
  • ServiceAccountCredential (39-39)
  • PkceOAuthCredential (40-40)
  • Credential (41-41)
  • AppCredential (42-42)
  • VaultConsentRecord (43-43)
  • VaultFederatedRecord (44-44)
  • PendingIncrementalAuth (45-45)
  • AuthorizationVault (47-47)
libs/sdk/src/auth/session/record/session.base.ts (1)
  • id (151-153)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (36)
libs/utils/src/storage/__tests__/typed-storage.test.ts (9)

1-36: Well-structured test setup.

The test infrastructure is clean with proper lifecycle management (connect/disconnect) and a well-defined test schema that matches the TypeScript interface.


38-56: LGTM!

Constructor tests properly verify instantiation with various configuration options and include instanceof checks as required by the coding guidelines.


96-112: LGTM!

Delete tests comprehensively cover both successful deletion and non-existent key scenarios with proper verification.


114-127: LGTM!

Exists tests cover both positive and negative cases appropriately.


129-156: LGTM!

Bulk operation tests cover the essential scenarios including mixed results (present/missing keys) and empty array edge cases.


177-199: LGTM!

Expire/TTL tests properly cover both existing and non-existent key scenarios with appropriate assertions.


201-227: LGTM!

Keys/count tests cover pattern matching well, and using sort() for comparison is a good practice for non-deterministic ordering.


268-294: LGTM!

Custom serialization test effectively verifies that user-provided serializer/deserializer functions are invoked and data integrity is maintained through the round-trip.


296-300: LGTM!

Raw access test correctly verifies the underlying adapter is exposed via the raw property, aligning with the implementation shown in the relevant code snippets.

libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts (4)

1-40: LGTM!

Test setup is well-structured with proper initialization and cleanup. Using randomBytes(32) for key generation is appropriate for test scenarios. Based on learnings, this approach avoids hardcoding encryption keys.


79-203: LGTM!

Comprehensive coverage of CRUD operations including encryption verification, null handling for non-existent keys, TTL support, and batch operations. The test at lines 86-91 that verifies raw storage contents is a good practice for validating actual encryption.


428-530: LGTM!

Good coverage of utility methods (keys, count, expire, ttl, raw) and complex data type handling. The nested object test at lines 498-518 properly validates that encryption/decryption preserves complex structures.


532-789: Excellent client-side key binding test coverage!

This section thoroughly tests critical security features including:

  • Session isolation (lines 740-772) ensuring different sessions cannot access each other's data
  • Domain separation via info parameter (lines 637-653)
  • Key rotation with client binding (lines 700-738)
  • Error type verification at line 787 correctly uses .toThrow(EncryptedStorageError)

The multi-session isolation test is particularly valuable for validating the security model.

libs/auth/src/session/token.store.ts (1)

1-41: LGTM!

Clean interface definition with proper TypeScript types. The TokenStore abstraction is well-documented with example usage, and the SecretRecord type appropriately wraps the encrypted blob with metadata.

libs/auth/src/session/storage/storage-token-store.ts (1)

124-132: Verify TTL behavior for already-expired tokens.

When exp is in the past, calculateTtl returns 1 (minimum TTL). This means an already-expired token blob will persist for 1 second before the storage backend expires it. Consider whether storing expired tokens at all is the intended behavior, or if put should reject/skip them.

libs/auth/src/jwks/jwks.types.ts (1)

33-34: Good improvement: Stricter typing for JWT header and payload.

Changing from any to Record<string, unknown> aligns with the strict TypeScript guidelines. Note this may require consumers to add type guards or assertions when accessing specific claims.

libs/auth/src/ui/templates.ts (2)

77-81: LGTM: Proper HTML escaping re-exported.

Good practice to re-export escapeHtml from the base layout, ensuring consistent XSS protection across all templates.


122-176: Good XSS protection in app card template.

All dynamic content (appId, appName, description, iconUrl, scopes, csrfToken, pendingAuthId, callbackPath) is properly escaped using escapeHtml(). This prevents XSS attacks from malicious app names or URLs.

libs/auth/src/jwks/jwks.service.ts (1)

449-453: Remove concern about createPrivateKey — no utility alternative exists in @frontmcp/utils.

@frontmcp/utils does not export a wrapper for createPrivateKey or other key object constructors. While it provides generateRsaKeyPair() for key generation, it lacks utilities for reconstructing KeyObjects from persisted JWK data, which is the specific use case here. This direct node:crypto usage is a necessary exception.

libs/auth/src/session/authorization-vault.ts (5)

1-26: LGTM on module structure and imports.

The module header clearly documents the purpose, supported credential types, and stateful vs stateless modes. The Zod import is appropriate for schema definitions.


34-48: LGTM on credential type enum schema.

The credential type enum comprehensively covers common authentication mechanisms including OAuth, API keys, certificates, and cloud service accounts. The inline comments aid readability.


56-235: LGTM on credential schemas.

The individual credential schemas are well-structured with appropriate validation constraints (e.g., z.string().min(1) for required fields). The discriminated union using the type field enables type-safe credential handling.


256-285: LGTM on app credential schema.

The appCredentialSchema properly composes the credential union with metadata fields for lifecycle tracking (acquiredAt, lastUsedAt, validity, expiration). The optional userInfo object is appropriately scoped.


386-534: LGTM on AuthorizationVault interface.

The interface is comprehensive with clear method signatures for:

  • Core CRUD operations
  • Consent management
  • Incremental authorization lifecycle
  • Credential storage with consent-aware filtering
  • OAuth token refresh support

Method signatures are consistent with Zod-inferred types.

libs/auth/src/session/index.ts (1)

1-92: LGTM on session module barrel exports.

The file correctly re-exports all public APIs from submodules with clear logical groupings:

  • Authorization vault schemas and types
  • Vault encryption utilities
  • Token vault and store
  • Storage-backed implementations

The separation of value exports from type exports follows TypeScript best practices. As per coding guidelines, no backwards compatibility aliases are introduced.

libs/utils/src/index.ts (2)

131-152: LGTM on unified key persistence exports.

The new key persistence API is well-structured with:

  • Base types for different key data variants
  • Validation utilities (validateKeyData, parseKeyData)
  • Type guards (isSecretKeyData, isAsymmetricKeyData)
  • Factory functions with storage integration

The "(recommended)" annotation clearly guides users toward the newer API.


217-239: LGTM on storage exports expansion.

The additions appropriately expose:

  • EncryptedStorageError for typed error handling
  • FileSystemStorageAdapter for Node.js file-based storage
  • TypedStorage and EncryptedTypedStorage wrappers with their configuration types

This aligns with the PR's goal of providing storage-backed vault implementations.

libs/auth/src/session/storage/storage-authorization-vault.ts (4)

1-32: LGTM on module documentation and imports.

Clear documentation with usage example. Imports correctly use @frontmcp/utils for randomUUID and TypedStorage, following the coding guidelines.


180-214: LGTM on pending auth creation.

Properly generates UUID, calculates expiration from TTL, and stores the pending auth with the vault entry. The default TTL fallback to this.pendingAuthTtlMs is appropriate.


467-476: Potential double-prefixing issue in key generation.

The key() method (lines 467-469) adds the namespace prefix only when storage is NOT namespaced. However, the constructor (line 73) already creates a namespaced view if isNamespacedStorage is true. This means:

  • If storage is NamespacedStorage: namespace is applied at line 73, and key() returns just id
  • If storage is StorageAdapter: namespace is applied at line 469

This logic appears correct, but verify the isNamespacedStorage check operates on the original storage, not the wrapped TypedStorage.


68-79: The concern in this review comment is not valid. TypedStorage<T> correctly exposes a raw getter property (lines 193–195 of typed-storage.ts) that returns the underlying StorageAdapter | NamespacedStorage. The line 468 usage (this.storage.raw) is correct and will work without errors.

Likely an incorrect or invalid review comment.

libs/auth/src/index.ts (5)

1-32: LGTM on package documentation.

Excellent module-level documentation with practical usage examples covering JWKS management, auth UI templates, and credential vault operations. This provides a clear entry point for library consumers.


50-82: LGTM on UI module exports.

Clean separation of layout utilities, template builders, and render functions. The type exports for theming configuration enable consumers to customize the auth UI.


83-167: LGTM on session module exports.

Comprehensive export of credential schemas, vault implementations, encryption utilities, and storage types. The grouping with inline comments aids discoverability.


169-192: LGTM on utils module exports.

WWW-Authenticate helpers and audience validation utilities are properly exposed with their associated types.


34-48: No action required—the dev-key functions are already properly marked with @deprecated JSDoc annotations in the source module (src/jwks/dev-key-persistence.ts) and re-exported in the barrel file. Consumers will see deprecation warnings directing them to use createKeyPersistence from @frontmcp/utils instead.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts:
- Around line 161-175: The has() method currently returns true if
this.cache.has(providerName) without validating the cached credential; change it
to retrieve the cached entry (this.cache.get(providerName)), run
this.isValid(cached) and only return true if valid; if invalid/expired, remove
it from the cache (e.g., this.cache.delete(providerName)) and fall through to
the existing registry/vault check that uses this.loadFromVault(providerName,
config) and this.isValid(fromVault).
- Around line 294-298: Replace the direct Buffer base64 encoding with the
utility from @frontmcp/utils: in the 'basic' case (where credential.encodedValue
is used), import the base64 encoder (e.g., encodeBase64) from '@frontmcp/utils'
and use encodeBase64(`${credential.username}:${credential.password}`) instead of
Buffer.from(...).toString('base64'); keep the same fallback logic
(credential.encodedValue ?? ...) and return { Authorization: `Basic ${encoded}`
} as before.

In @libs/sdk/src/auth/vault/auth-providers.vault.ts:
- Around line 250-264: In buildVaultKey, replace the generic throw new
Error(...) in the 'user' case with the project-specific MCP error class (e.g.,
throw new McpError({ code: 'MCP_INVALID_ARGUMENT', message: `userId is required
for user-scoped credentials (namespace: ${this.namespace})` })) and add the
corresponding import; also remove or tighten the redundant default branch
(either delete the default and let TypeScript exhaustiveness check handle
unknown CredentialScope or make default explicitly throw an McpError for an
unknown scope) so the intent is explicit.
🧹 Nitpick comments (12)
libs/sdk/src/auth/session/record/session.stateful.ts (1)

52-55: Redundant type cast.

StatefulCreateCtx is defined as BaseCreateCtx & {} (line 7), which is equivalent to BaseCreateCtx. The type assertion is unnecessary.

Suggested fix
  constructor(ctx: StatefulCreateCtx) {
-   super(ctx as BaseCreateCtx);
+   super(ctx);
    throw new InternalMcpError('StatefulSession not yet implemented', 'NOT_IMPLEMENTED');
  }
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (3)

78-102: Lost provider name context on rejection.

When a promise is rejected in getMany, the provider name associated with that failure is lost because Promise.allSettled only provides the rejection reason in outcome.reason. The comment on line 96 acknowledges this limitation.

Consider capturing the name in the rejection path:

Suggested improvement
     const promises = providerNames.map(async (name) => {
-      const result = await this.get(name, options);
-      return { name, result };
+      try {
+        const result = await this.get(name, options);
+        return { name, result };
+      } catch (error) {
+        throw { name, error };
+      }
     });
 
     const settled = await Promise.allSettled(promises);
 
     for (const outcome of settled) {
       if (outcome.status === 'fulfilled') {
         results.set(outcome.value.name, outcome.value.result);
       } else {
-        // Find the provider name from the error context if possible
-        this.logger?.warn('Failed to load credential:', outcome.reason);
+        const { name, error } = outcome.reason as { name: string; error: unknown };
+        this.logger?.warn(`Failed to load credential "${name}":`, error);
+        results.set(name, null);
       }
     }

118-127: Sequential header fetching in headersMany could be parallelized.

The method fetches headers sequentially using a for...of loop. Since this.headers() is async, this could be parallelized for better performance when fetching headers for multiple providers.

Suggested parallelization
   async headersMany(providerNames: string[]): Promise<Record<string, string>> {
     const allHeaders: Record<string, string> = {};
 
-    for (const name of providerNames) {
-      const headers = await this.headers(name);
-      Object.assign(allHeaders, headers);
-    }
+    const headerResults = await Promise.all(
+      providerNames.map((name) => this.headers(name)),
+    );
+    for (const headers of headerResults) {
+      Object.assign(allHeaders, headers);
+    }
 
     return allHeaders;
   }

140-144: Type assertion as Credential bypasses type safety.

The existingCredential is cast to Credential without validation. If existing?.credential has an incompatible shape, this could cause runtime issues in the loader's refresh logic.

Consider using a type guard or removing the assertion if the types are already compatible:

       const refreshed = await this.loader.refresh(config, {
         ...this.context,
-        existingCredential: existingCredential as Credential,
+        existingCredential,
       });

If the type mismatch is due to interface definitions, ensure CredentialFactoryContext.existingCredential accepts Credential | undefined.

libs/sdk/src/auth/vault/auth-providers.vault.ts (1)

190-240: Empty catch blocks silently swallow errors.

The getAllCredentials method has multiple empty catch blocks (lines 196-198, 211-213, 223-225, 235-237) that silently ignore errors. While this provides resilience, it makes debugging difficult if one scope's retrieval fails.

Consider at minimum logging at debug level:

Suggested improvement
     // Session scope
     try {
       const sessionCreds = await this.baseVault.getAppCredentials(
         this.buildVaultKey(sessionId, 'session'),
         this.namespace,
       );
       credentials.push(...sessionCreds);
-    } catch {
-      // Ignore
+    } catch (error) {
+      this.logger?.debug('Failed to get session-scoped credentials:', error);
     }
libs/auth/src/session/vault-encryption.ts (3)

110-116: Consider storing pepper as string to avoid redundant encoding/decoding.

The pepper is converted from string to Uint8Array here (line 115), but then decoded back to string when building IKM (lines 137, 170). Consider keeping it as a string internally since it's always used in string concatenation.

♻️ Suggested simplification
 export class VaultEncryption {
-  private readonly pepper: Uint8Array;
+  private readonly pepper: string;
   private readonly hkdfInfo: string;

   constructor(config: VaultEncryptionConfig = {}) {
-    // Convert pepper to Uint8Array, use empty if not provided
-    this.pepper = new TextEncoder().encode(config.pepper ?? '');
+    this.pepper = config.pepper ?? '';
     this.hkdfInfo = config.hkdfInfo ?? 'frontmcp-vault-v1';
   }

Then in deriveKey and deriveKeyFromToken, use this.pepper directly instead of new TextDecoder().decode(this.pepper).


132-144: Add delimiters to IKM construction to prevent potential collisions.

Joining IKM parts with an empty separator could theoretically cause collisions if claim values contain overlapping characters at boundaries. For example: jti="ab" + vaultKey="cd" produces the same IKM prefix as jti="abc" + vaultKey="d".

While the practical risk is low given structured claim formats (UUIDs, user IDs), adding a delimiter like \x00 (null byte) between parts is a cryptographic best practice.

♻️ Suggested improvement
     const ikmParts = [
       claims.jti,
       claims.vaultKey ?? '',
       claims.sub,
       claims.iat.toString(),
-      new TextDecoder().decode(this.pepper),
+      this.pepper,
     ];
-    const ikm = new TextEncoder().encode(ikmParts.join(''));
+    const ikm = new TextEncoder().encode(ikmParts.join('\x00'));

143-143: Remove unnecessary async/await from methods using synchronous crypto functions.

The @frontmcp/utils crypto functions (hkdfSha256, encryptAesGcm, decryptAesGcm) are all synchronous and return values directly, not Promises. Methods deriveKey (line 143), deriveKeyFromToken (line 176), encrypt (line 196), and decrypt (line 235) can be refactored to remove the async keyword and unnecessary await calls, improving performance and code clarity.

libs/sdk/src/auth/session/encrypted-authorization-vault.ts (4)

110-116: Consider defining a minimal Redis interface instead of any.

While the eslint-disable comment is present, defining a typed interface for the required Redis methods (get, set, del) would improve type safety and documentation of the expected contract.

♻️ Suggested interface
interface RedisClient {
  get(key: string): Promise<string | null>;
  set(key: string, value: string): Promise<void>;
  del(key: string): Promise<void>;
}
 export class EncryptedRedisVault implements AuthorizationVault {
   constructor(
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    private readonly redis: any,
+    private readonly redis: RedisClient,
     private readonly encryption: VaultEncryption,
     private readonly namespace = 'vault:',
   ) {}

183-201: Type assertions on decrypted data bypass runtime safety.

The as casts on lines 194-197 assume the decrypted data matches the expected types. If decryptObject returns unvalidated data, corrupted or tampered encrypted blobs could cause runtime issues. Consider validating the decrypted sensitive data against a schema.

♻️ Suggested validation approach
// Define a schema for VaultSensitiveData if not already exported
const vaultSensitiveDataSchema = z.object({
  appCredentials: z.record(z.string(), appCredentialSchema),
  consent: vaultConsentRecordSchema.optional(),
  federated: vaultFederatedRecordSchema.optional(),
  pendingAuths: z.array(pendingIncrementalAuthSchema),
});

// In toVaultEntry:
const sensitive = vaultSensitiveDataSchema.parse(
  await this.decryptSensitive(redisEntry.encrypted)
);

240-251: Consider preserving error cause for better debugging.

The error handling extracts only the message, losing the original stack trace. Using the cause option preserves the error chain for debugging.

♻️ Preserve error cause
     } catch (error) {
       // Could be decryption failure (wrong key) or corrupt data
-      throw new Error(`Failed to load vault ${id}: ${error instanceof Error ? error.message : 'Unknown error'}`);
+      throw new Error(`Failed to load vault ${id}`, { cause: error });
     }

528-542: Avoid non-null assertion on line 537.

While the logic ensures entry.consent exists at this point (checked on line 532), using ! violates coding guidelines. A type guard or restructuring would be cleaner.

♻️ Remove non-null assertion
   async shouldStoreCredential(vaultId: string, appId: string, toolIds?: string[]): Promise<boolean> {
     const entry = await this.loadEntry(vaultId);
     if (!entry) return false;

-    if (!entry.consent?.enabled) {
+    const consent = entry.consent;
+    if (!consent?.enabled) {
       return true;
     }

     if (toolIds && toolIds.length > 0) {
-      return toolIds.some((toolId) => entry.consent!.selectedToolIds.includes(toolId));
+      return toolIds.some((toolId) => consent.selectedToolIds.includes(toolId));
     }

-    const consentedToolIds = entry.consent.selectedToolIds;
+    const consentedToolIds = consent.selectedToolIds;
     return consentedToolIds.some((toolId) => toolId.startsWith(`${appId}:`));
   }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9260c23 and 172607b.

📒 Files selected for processing (8)
  • libs/auth/src/session/vault-encryption.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/auth/src/session/vault-encryption.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/auth/src/session/vault-encryption.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/auth/src/session/vault-encryption.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/auth/src/session/vault-encryption.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/auth/src/session/vault-encryption.ts
  • libs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • libs/auth/src/session/vault-encryption.ts
🧬 Code graph analysis (2)
libs/auth/src/session/vault-encryption.ts (1)
libs/utils/src/crypto/index.ts (6)
  • hkdfSha256 (96-104)
  • randomBytes (57-62)
  • encryptAesGcm (113-125)
  • base64urlEncode (182-197)
  • base64urlDecode (202-220)
  • decryptAesGcm (135-146)
libs/sdk/src/auth/session/record/session.stateful.ts (1)
libs/sdk/src/auth/session/record/session.base.ts (1)
  • BaseCreateCtx (8-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (24)
libs/sdk/src/auth/session/record/session.stateful.ts (2)

4-5: LGTM! Imports align with architecture goals.

The switch to @frontmcp/auth for TokenVault follows the package extraction pattern, and using InternalMcpError with MCP error codes aligns with the SDK error handling guidelines.


57-59: LGTM!

Correct use of underscore prefix for unused parameter and proper MCP-specific error handling.

libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (4)

21-33: LGTM - Well-structured constructor with dependency injection.

The constructor properly encapsulates dependencies and creates a child logger for tracing. The use of readonly ensures immutability of injected dependencies.


35-76: Consider handling the case where loader returns a non-null value but caching fails.

The get method has a good layered approach (cache → vault → loader). However, if storeInVault fails silently at line 72 (it catches and logs), the credential is still cached and returned. This is acceptable defensive behavior, but worth noting that vault and cache can become inconsistent.

One minor concern: at line 75, the type assertion as ResolvedCredential<T> assumes loaded matches T, but the loader returns a generic ResolvedCredential. This should be safe if the registry and loader are consistent, but verify the type flow.


207-238: LGTM - Robust vault loading with proper error handling.

The loadFromVault method correctly catches errors and returns null as a fallback. The reconstructed ResolvedCredential shape is reasonable, though the acquiredAt: Date.now() comment correctly notes the limitation of not persisting acquisition time.


264-274: LGTM - Clean validity check.

The isValid method properly checks both the explicit isValid flag and expiry time.

libs/sdk/src/auth/vault/auth-providers.vault.ts (6)

17-26: LGTM - Clean constructor with sensible defaults.

The namespace default 'authproviders:' and child logger pattern are well-designed.


37-62: LGTM - Proper credential storage with error propagation.

The storeCredential method correctly constructs the AppCredential shape with expiry extraction and re-throws on failure, which is appropriate for write operations where callers need to know about failures.


73-104: LGTM - Defensive credential retrieval with validity checks.

The method properly checks both the isValid flag and expiry time before returning credentials. Returning null on errors is appropriate for read operations.


114-128: Inconsistent error handling: removeCredential swallows errors.

Unlike storeCredential which re-throws, removeCredential only logs the error. If removal fails, the caller won't know and may assume the credential was successfully removed. Consider either re-throwing or documenting this as intentional "best-effort" removal.


165-181: LGTM - OAuth refresh with proper error propagation.

The method correctly re-throws on failure, consistent with storeCredential behavior for write operations.


267-270: Prefer Token.for() pattern if available in @frontmcp/di.

The DI token is created using Symbol.for() with a cast. Verify this matches the project's DI token conventions. If @frontmcp/di provides a Token.for() or similar factory method, that would be more type-safe.

libs/auth/src/session/vault-encryption.ts (6)

24-31: LGTM! Correctly uses @frontmcp/utils for cryptographic operations.

This aligns with the coding guidelines and learnings that require using @frontmcp/utils for cryptographic operations instead of node:crypto directly, ensuring cross-platform compatibility.


158-178: LGTM! Including JWT signature provides additional entropy.

The method correctly includes the JWT signature as additional entropy for key derivation. Note: The same delimiter suggestion from deriveKey applies to the IKM construction here (line 172).


187-205: LGTM! Encryption implementation is correct.

  • Proper 32-byte key validation
  • Correct 12-byte IV for AES-GCM
  • Base64url encoding for safe storage

215-241: LGTM! Decryption implementation is secure.

  • Proper schema validation before decryption
  • Generic error message prevents leaking cryptographic details
  • Correct use of decryptAesGcm with tag authentication

250-264: LGTM! Generic encryption helpers are convenient.

The as T cast in decryptObject is a common pattern. Consider documenting that callers should validate the returned object if type safety is critical.


40-51: LGTM! Encrypted data schema is well-designed.

Version field (v: z.literal(1)) enables future algorithm migrations. The structure properly separates IV, ciphertext, and authentication tag.

libs/sdk/src/auth/session/encrypted-authorization-vault.ts (6)

26-41: LGTM! Import consolidation aligns with the new @frontmcp/auth package structure.

The imports correctly use @frontmcp/utils for randomUUID and consolidate vault-related types from @frontmcp/auth. Based on learnings, this follows the project convention of using @frontmcp/utils for common utilities.


143-150: LGTM! Clear fail-fast behavior with helpful error message.

The method correctly retrieves the key from AsyncLocalStorage and provides a descriptive error when the context is not set, guiding users to the correct usage pattern.


206-227: LGTM! Clean async transformation for Redis entry conversion.

The method correctly awaits the encryption operation and constructs the Redis entry with proper separation of encrypted and unencrypted fields.


594-606: Factory function mirrors constructor's any type for redis.

Same suggestion applies here - consider using the Redis interface type if defined.


169-178: LGTM! Async transformation of encryption methods.

The async conversion is correct and necessary—VaultEncryption.encryptObject() and decryptObject() both return Promise<EncryptedData> and Promise<T> respectively. The methods properly delegate to these Promise-based operations and correctly preserve generic typing with VaultSensitiveData.


85-90: Uint8Array for encryption keys is correct and properly used throughout the codebase.

The type change from Buffer to Uint8Array improves portability and aligns with the @frontmcp/utils cryptographic operations. All key derivation methods (deriveKey, deriveKeyFromToken) and encryption/decryption operations correctly return and accept Uint8Array. Since EncryptionContext is an internal implementation detail not exported from the public SDK API, this is not a breaking change for consumers.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts:
- Around line 232-239: The comment in loadFromVault is misleading about
acquiredAt; either clarify that acquiredAt reflects the load time by changing
the comment in auth-providers.accessor.impl.ts to "// acquiredAt is set to load
time; original acquisition time is not preserved from vault", or if you intended
to preserve original acquisition time, update getCredential in
auth-providers.vault.ts to return the stored AppCredential metadata (including
acquiredAt) and have loadFromVault use that stored acquiredAt instead of
Date.now(); reference storeCredential, getCredential, loadFromVault and
acquiredAt when making the change.
🧹 Nitpick comments (3)
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (3)

93-100: Incomplete error context for rejected promises.

When a credential load fails, the provider name is lost because the rejection handler can't reliably extract it from outcome.reason. Consider restructuring to preserve the provider name.

♻️ Suggested improvement
     for (const outcome of settled) {
       if (outcome.status === 'fulfilled') {
         results.set(outcome.value.name, outcome.value.result);
       } else {
-        // Find the provider name from the error context if possible
-        this.logger?.warn('Failed to load credential:', outcome.reason);
+        // Note: provider name context is lost in rejection
+        // Consider wrapping errors in loader to include provider name
+        this.logger?.warn('Failed to load credential for a provider:', outcome.reason);
       }
     }

Alternatively, wrap errors in the promise map to preserve context:

const promises = providerNames.map(async (name) => {
  try {
    const result = await this.get(name, options);
    return { name, result };
  } catch (error) {
    throw { name, error }; // Preserve name in rejection
  }
});

119-128: Sequential header fetching in headersMany.

The method fetches headers sequentially. If multiple providers are typically requested together, parallel fetching could improve performance. However, the current sequential approach is simpler and avoids header key collision complexity.

♻️ Optional parallel optimization
   async headersMany(providerNames: string[]): Promise<Record<string, string>> {
-    const allHeaders: Record<string, string> = {};
-
-    for (const name of providerNames) {
-      const headers = await this.headers(name);
-      Object.assign(allHeaders, headers);
-    }
-
-    return allHeaders;
+    const headerPromises = providerNames.map((name) => this.headers(name));
+    const headerResults = await Promise.all(headerPromises);
+    return Object.assign({}, ...headerResults);
   }

249-265: Silent vault storage failure may cause credential loss on restart.

If storeInVault fails (e.g., Redis unavailable), the credential exists only in the in-memory cache and will be lost on restart. This resilience trade-off is reasonable but worth documenting or monitoring.

Consider tracking vault write failures via metrics or structured logs with a specific code for observability.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 172607b and c129a2b.

📒 Files selected for processing (4)
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/utils/src/crypto/index.ts
  • libs/utils/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/utils/src/crypto/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/utils/src/index.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/utils/src/index.ts
libs/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Files:

  • libs/utils/src/index.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/utils/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/utils/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/utils/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errors

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/utils/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/utils/src/index.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Build Libraries
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (18)
libs/utils/src/index.ts (3)

84-85: Good addition for completeness.

Adding standard base64 encoding/decoding alongside the existing base64url variants provides a complete encoding surface for consumers.


219-240: Comprehensive storage API expansion.

The new storage exports are well-organized:

  • FileSystemStorageAdapter follows the established adapter pattern (consistent with Redis, VercelKv, Upstash adapters).
  • TypedStorage and EncryptedTypedStorage provide progressive type safety and encryption layers.
  • Associated types (EncryptionKey, StoredEncryptedBlob, ClientKeyBinding) properly support the encryption features.

This aligns with the coding guidelines recommendation to use @frontmcp/utils for file system operations.


116-153: Add documentation for the new KeyPersistence API to docs/draft/docs/**.

The new KeyPersistence class and factory functions (createKeyPersistence, createKeyPersistenceWithStorage) are public API additions to the utils library. Per coding guidelines, public API changes require matching documentation updates in docs/draft/docs/**. Currently, no documentation exists for this new API surface.

libs/sdk/src/auth/vault/auth-providers.vault.ts (9)

1-27: LGTM! Clean module setup and constructor.

The class is well-structured with proper dependency injection, optional logger with child context, and clear documentation. The use of InvalidInputError (imported from ../../errors/mcp.error) aligns with the coding guideline to use specific error classes with MCP error codes.


38-63: LGTM!

The storeCredential method correctly builds the vault key, constructs the AppCredential with proper metadata (acquiredAt, expiresAt), and appropriately re-throws errors after logging to allow callers to handle failures.


74-105: LGTM!

The getCredential method properly validates credential state (checking both isValid flag and expiration) before returning. The error handling approach of logging and returning null is appropriate for a retrieval operation where absence is a valid outcome.


115-129: LGTM!

The silent error handling for removeCredential is acceptable for a cleanup operation where a best-effort approach is appropriate. The warning log ensures visibility while not disrupting the caller flow.


140-155: LGTM!

The invalidation method correctly marks credentials as invalid while preserving them for audit/debugging purposes. The reason parameter provides good traceability.


166-182: LGTM!

The refreshOAuthCredential method correctly re-throws errors since token refresh is a critical operation where failures should propagate to the caller for proper handling.


191-241: LGTM!

The getAllCredentials method correctly aggregates credentials from all applicable scopes. The silent error handling for individual scope queries is appropriate since partial results are better than failing entirely.


251-268: LGTM!

Excellent use of the exhaustive switch pattern with never type to ensure compile-time safety when new scopes are added. The key construction is well-documented and uses the appropriate InvalidInputError class per coding guidelines.


271-274: LGTM!

The DI token follows the established pattern using Symbol.for for cross-module consistency with proper type casting.

libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (6)

1-34: LGTM! Good use of @frontmcp/utils for cryptographic operations.

The import of base64Encode from @frontmcp/utils (line 9) follows the coding guideline to use @frontmcp/utils for cryptographic operations instead of node:crypto directly. The constructor properly injects all required dependencies.


36-77: LGTM!

The get method implements a correct multi-tier lookup strategy (cache → vault → loader) with proper handling of the forceRefresh option. The flow correctly caches results after loading from vault or factory.


130-160: LGTM!

The refresh method correctly retrieves the existing credential for context, delegates to the loader, and updates both vault and cache on success. Error handling with warning logs is appropriate.


162-211: LGTM!

The has method correctly validates and invalidates expired cache entries. The utility methods (isRegistered, invalidate, invalidateAll, listProviders, listAvailable) properly delegate to the appropriate underlying components.


270-280: LGTM!

The isValid helper correctly validates both the validity flag and expiration timestamp.


285-319: LGTM! Proper use of @frontmcp/utils for encoding.

The defaultHeaders method correctly uses base64Encode from @frontmcp/utils for Basic auth encoding, adhering to the coding guideline about cryptographic operations. The OAuth token type is properly taken from the credential rather than hardcoding "Bearer".

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @libs/auth/src/session/session-crypto.ts:
- Around line 106-118: decryptAesGcmSync currently requires Node's crypto at
runtime and uses CommonJS require; replace that with the same ES-module or
injected crypto usage as encryptAesGcmSync to avoid direct node:crypto require
calls—remove the inline require('crypto') in decryptAesGcmSync, use the
shared/imported crypto API (e.g., the createDecipheriv function from
'node:crypto' or the passed-in crypto instance used elsewhere), keep the
existing logic (setAuthTag, update/final, Buffer.concat) and ensure types remain
correct for key/iv/tag/data and the function signature.

In @libs/sdk/src/auth/vault/auth-providers.vault.ts:
- Around line 100-103: The catch in getCredential currently logs and returns
null, hiding storage failures; update getCredential to re-throw storage errors
using the same pattern as addAppCredential/refreshOAuthCredential by wrapping or
converting the caught error into an InternalMcpError (or re-throwing if already
that type) so callers can distinguish "not found" from storage failure; keep
logging but throw InternalMcpError with context including providerId and
original error instead of returning null for non-not-found errors.
🧹 Nitpick comments (14)
libs/sdk/src/auth/machine-id.ts (2)

53-56: Strengthen UUID validation regex.

The current regex /^[0-9a-f-]{32,36}$/i is too permissive and would match invalid patterns like "------------------------------------" (36 dashes). Consider using a proper UUID v4 pattern or at minimum enforce the 8-4-4-4-12 structure.

Suggested stricter UUID regex
-    // Validate UUID format (loose check)
-    if (/^[0-9a-f-]{32,36}$/i.test(content)) {
+    // Validate UUID format (8-4-4-4-12 structure)
+    if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(content)) {
       return content;
     }

85-86: Use type guard for error handling consistency.

Same issue as in loadMachineIdSync - use a type guard instead of type assertion.

Suggested fix
     } catch (error) {
-      console.warn(`[MachineId] Failed to save to ${machineIdPath}: ${(error as Error).message}`);
+      const message = error instanceof Error ? error.message : String(error);
+      console.warn(`[MachineId] Failed to save to ${machineIdPath}: ${message}`);
     }
libs/auth/src/vault/credential-cache.ts (2)

47-63: Redundant credential expiration check.

The isExpired() call on line 48 already checks credential expiration via isExpiredAt() (see lines 208-210). The second check on lines 57-63 is redundant and can be removed.

♻️ Suggested simplification
     // Check TTL expiration
     if (this.isExpired(entry)) {
       this.cache.delete(providerId);
       this.stats.size = this.cache.size;
       this.stats.misses++;
       this.stats.evictions++;
       return undefined;
     }
 
-    // Check credential expiration
-    if (entry.resolved.expiresAt && Date.now() >= entry.resolved.expiresAt) {
-      this.cache.delete(providerId);
-      this.stats.size = this.cache.size;
-      this.stats.misses++;
-      this.stats.evictions++;
-      return undefined;
-    }
-
     this.stats.hits++;

139-146: Consider using a separate array for keys to delete when iterating.

Modifying a Map while iterating over it with for...of is technically safe in modern JavaScript (the iterator will see items deleted during iteration), but collecting keys first is more explicit and avoids potential confusion.

♻️ Suggested clarification
   invalidateByScope(scope: CredentialScope): void {
+    const keysToDelete: string[] = [];
     for (const [key, entry] of this.cache) {
       if (entry.resolved.scope === scope) {
-        this.cache.delete(key);
+        keysToDelete.push(key);
       }
     }
+    for (const key of keysToDelete) {
+      this.cache.delete(key);
+    }
     this.stats.size = this.cache.size;
   }
libs/auth/src/vault/auth-providers.types.ts (1)

319-327: Consider whether Error type in CredentialEvent should be serializable.

The error?: Error field may not serialize well if events are logged or transmitted. Consider using a structured error type with message, code, and optional stack fields for better observability.

libs/auth/src/authorization/authorization.types.ts (1)

1-256: Well-structured authorization types with comprehensive runtime validation.

The types and schemas are well-documented and follow a consistent pattern. A few observations:

  1. AuthorizationCreateCtx (lines 227-256) lacks a corresponding Zod schema, unlike all other public types in this file. If runtime validation is needed for this type at API boundaries, consider adding an authorizationCreateCtxSchema.

  2. AuthorizedTool and AuthorizedPrompt (and their schemas) are structurally identical. If this is intentional for semantic distinction, consider a shared base type to reduce duplication.

💡 Optional: Add schema for AuthorizationCreateCtx if needed at runtime boundaries
export const authorizationCreateCtxSchema = z.object({
  id: z.string(),
  isAnonymous: z.boolean(),
  user: authUserSchema,
  claims: z.record(z.string(), z.unknown()).optional(),
  expiresAt: z.number().optional(),
  scopes: z.array(z.string()).optional(),
  token: z.string().optional(),
  authorizedApps: z.record(z.string(), z.object({ id: z.string(), toolIds: z.array(z.string()) })).optional(),
  authorizedAppIds: z.array(z.string()).optional(),
  authorizedTools: z.record(z.string(), authorizedToolSchema).optional(),
  authorizedToolIds: z.array(z.string()).optional(),
  authorizedPrompts: z.record(z.string(), authorizedPromptSchema).optional(),
  authorizedPromptIds: z.array(z.string()).optional(),
  authorizedResources: z.array(z.string()).optional(),
});
libs/sdk/src/auth/session/transport-session.manager.ts (1)

16-16: Using deprecated sync crypto functions.

The encryptAesGcmSync and decryptAesGcmSync functions are marked as deprecated in session-crypto.ts (with a note to use async variants instead). Consider refactoring encodeSessionJwt and decryptSessionJwt to be async methods, which would allow using the preferred async crypto functions and improve cross-platform compatibility.

If async refactoring is not feasible now, please verify that the sync crypto path (which uses node:crypto directly) meets your cross-platform requirements, as per the coding guidelines recommending @frontmcp/utils for cross-platform crypto support.

libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (1)

123-132: Consider parallelizing headersMany like getMany.

The sequential await in the loop could be parallelized for better performance when fetching headers for multiple providers.

♻️ Optional parallel implementation
   async headersMany(providerNames: string[]): Promise<Record<string, string>> {
     const allHeaders: Record<string, string> = {};

-    for (const name of providerNames) {
-      const headers = await this.headers(name);
-      Object.assign(allHeaders, headers);
-    }
+    const headerResults = await Promise.all(
+      providerNames.map((name) => this.headers(name))
+    );
+
+    for (const headers of headerResults) {
+      Object.assign(allHeaders, headers);
+    }

     return allHeaders;
   }
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (3)

33-53: In-flight deduplication returns stale promise on generic type mismatch.

The load method stores the promise in a Map<string, Promise<ResolvedCredential | null>> (line 18), but retrieves and casts it to Promise<ResolvedCredential<T> | null> at line 41. If two callers request the same provider with different generic type parameters T, the second caller receives a promise typed incorrectly.

This is safe at runtime since the credential type is determined by the provider config, but the cast at line 41 (as Promise<ResolvedCredential<T> | null>) bypasses TypeScript's type checking. Consider documenting this behavior or storing provider-specific type information.


156-161: cancelAll() only clears the map without canceling pending operations.

The method name implies cancellation, but it only clears the in-flight map. The actual promises continue executing, and their results are discarded. This could lead to:

  1. Wasted resources from completing operations that are no longer tracked
  2. Potential state inconsistencies if factory functions have side effects

Consider renaming to clearPending() or adding a comment clarifying the behavior does not actually abort in-flight operations.

📝 Suggested documentation improvement
   /**
-   * Cancel all in-flight loads (for cleanup)
+   * Clear all in-flight load tracking (for cleanup).
+   * Note: This does not abort executing promises - they will complete
+   * but their results will not be tracked or deduplicated.
    */
   cancelAll(): void {
     this.loading.clear();
   }

103-144: Refresh operation lacks in-flight deduplication.

Unlike load(), the refresh() method does not deduplicate concurrent refresh calls for the same provider. If multiple callers refresh the same credential simultaneously, they will all execute the refresh function independently, potentially causing race conditions or wasted API calls.

Consider applying the same deduplication pattern used in load() if concurrent refreshes are expected.

libs/sdk/src/auth/vault/auth-providers.vault.ts (1)

190-240: Silent error handling in getAllCredentials may mask storage issues.

The method catches and ignores errors when fetching credentials from each scope (lines 196-198, 211-213, 223-225, 235-237). While this allows partial results, it could hide systematic storage failures.

Consider logging these errors at debug level to aid troubleshooting, or returning a result object that indicates which scopes succeeded/failed.

📝 Suggested improvement for observability
     // Session scope
     try {
       const sessionCreds = await this.baseVault.getAppCredentials(
         this.buildVaultKey(sessionId, 'session'),
         this.namespace,
       );
       credentials.push(...sessionCreds);
     } catch {
-      // Ignore
+      this.logger?.debug('Failed to fetch session-scoped credentials');
     }
libs/auth/src/index.ts (2)

180-201: Mixed export syntax: inline type keyword and separate export type.

The authorization module exports mix inline type exports (type AuthMode, type AuthUser, etc.) with value exports (AppAuthState, schemas) in the same block. While valid TypeScript, this differs from other sections that use separate export type { ... } blocks.

Consider using the same pattern as other sections for consistency:

📝 Suggested consistency improvement
 export {
-  // Types
-  type AuthMode,
-  type AuthUser,
-  type AuthorizedTool,
-  type AuthorizedPrompt,
-  type LLMSafeAuthContext,
-  type AppAuthorizationRecord,
-  type ProgressiveAuthState,
-  type AuthorizationCreateCtx,
   // Enums
   AppAuthState,
   // Schemas
   authModeSchema,
   authUserSchema,
   authorizedToolSchema,
   authorizedPromptSchema,
   llmSafeAuthContextSchema,
   appAuthStateSchema,
   appAuthorizationRecordSchema,
   progressiveAuthStateSchema,
 } from './authorization';
+export type {
+  AuthMode,
+  AuthUser,
+  AuthorizedTool,
+  AuthorizedPrompt,
+  LLMSafeAuthContext,
+  AppAuthorizationRecord,
+  ProgressiveAuthState,
+  AuthorizationCreateCtx,
+} from './authorization';

228-260: Vault module exports also use mixed inline type syntax.

Similar to the authorization section, the vault module exports use inline type keywords. Consider aligning with the separate export type { ... } pattern used elsewhere for consistency across the file.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c129a2b and b44d34d.

📒 Files selected for processing (38)
  • libs/auth/docs/AUTH.md
  • libs/auth/docs/FLOW_CHART.md
  • libs/auth/docs/REQUIREMENTS.md
  • libs/auth/docs/ROADMAP.md
  • libs/auth/src/authorization/authorization.types.ts
  • libs/auth/src/authorization/index.ts
  • libs/auth/src/index.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/session/session-crypto.ts
  • libs/auth/src/vault/auth-providers.types.ts
  • libs/auth/src/vault/credential-cache.ts
  • libs/auth/src/vault/credential-helpers.ts
  • libs/auth/src/vault/index.ts
  • libs/sdk/src/auth/authorization/authorization.types.ts
  • libs/sdk/src/auth/machine-id.ts
  • libs/sdk/src/auth/oauth/flows/oauth.authorize.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.device-authorization.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.introspect.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.par.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.revoke.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.token.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.userinfo.flow.ts
  • libs/sdk/src/auth/oauth/flows/oidc.logout.flow.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/session.crypto.ts
  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/auth/session/token.store.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.ts
  • libs/sdk/src/auth/vault/auth-providers.providers.ts
  • libs/sdk/src/auth/vault/auth-providers.registry.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/vault/credential-cache.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/sdk/src/auth/vault/index.ts
💤 Files with no reviewable changes (11)
  • libs/sdk/src/auth/oauth/flows/oauth.userinfo.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.token.flow.ts
  • libs/sdk/src/auth/session/session.crypto.ts
  • libs/sdk/src/auth/oauth/flows/oauth.par.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.introspect.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.device-authorization.flow.ts
  • libs/sdk/src/auth/oauth/flows/oidc.logout.flow.ts
  • libs/sdk/src/auth/oauth/flows/oauth.authorize.flow.ts
  • libs/sdk/src/auth/session/token.store.ts
  • libs/sdk/src/auth/oauth/flows/oauth.revoke.flow.ts
  • libs/auth/docs/REQUIREMENTS.md
✅ Files skipped from review due to trivial changes (1)
  • libs/auth/docs/FLOW_CHART.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • libs/sdk/src/auth/vault/auth-providers.providers.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
  • libs/sdk/src/auth/vault/auth-providers.registry.ts
  • libs/sdk/src/auth/session/token.refresh.ts
  • libs/sdk/src/auth/vault/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/vault/credential-cache.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/auth/src/vault/credential-helpers.ts
  • libs/sdk/src/auth/machine-id.ts
  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.ts
  • libs/auth/src/authorization/index.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/auth/src/vault/index.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/auth/src/vault/credential-cache.ts
  • libs/auth/src/session/session-crypto.ts
  • libs/sdk/src/auth/authorization/authorization.types.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/auth/src/vault/auth-providers.types.ts
  • libs/auth/src/authorization/authorization.types.ts
  • libs/auth/src/index.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/vault/credential-cache.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/sdk/src/auth/machine-id.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/authorization/authorization.types.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/vault/credential-cache.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/sdk/src/auth/machine-id.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/authorization/authorization.types.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/vault/credential-cache.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/auth/src/vault/credential-helpers.ts
  • libs/sdk/src/auth/machine-id.ts
  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/vault/auth-providers.accessor.ts
  • libs/auth/src/authorization/index.ts
  • libs/auth/docs/AUTH.md
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/auth/src/vault/index.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/auth/src/vault/credential-cache.ts
  • libs/auth/src/session/session-crypto.ts
  • libs/sdk/src/auth/authorization/authorization.types.ts
  • libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
  • libs/auth/src/vault/auth-providers.types.ts
  • libs/auth/src/authorization/authorization.types.ts
  • libs/auth/src/index.ts
libs/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Files:

  • libs/auth/src/index.ts
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/machine-id.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/auth/src/session/session-crypto.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/machine-id.ts
  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/auth/src/session/session-crypto.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/machine-id.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/auth/src/session/session-crypto.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/sdk/src/auth/machine-id.ts
  • libs/sdk/src/auth/authorization/authorization.types.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/authorization/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Applied to files:

  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/authorization/index.ts
  • libs/auth/src/vault/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files

Applied to files:

  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/auth/src/session/index.ts
  • libs/auth/src/authorization/index.ts
  • libs/auth/src/vault/index.ts
  • libs/sdk/src/auth/authorization/authorization.types.ts
  • libs/auth/src/vault/auth-providers.types.ts
  • libs/auth/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality

Applied to files:

  • libs/sdk/src/auth/vault/credential-loaders/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errors

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods

Applied to files:

  • libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/auth/src/session/session-crypto.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use `getCapabilities()` for dynamic capability exposure instead of hardcoding capabilities

Applied to files:

  • libs/sdk/src/auth/authorization/authorization.types.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{validation,**}/**/*.{ts,tsx} : Use `.strict()` on all Zod schemas for validation

Applied to files:

  • libs/auth/src/vault/auth-providers.types.ts
  • libs/auth/src/authorization/authorization.types.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx,js,jsx} : Do not expose internal error details in public APIs - use sanitized error messages

Applied to files:

  • libs/auth/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/auth/src/index.ts
🧬 Code graph analysis (3)
libs/sdk/src/auth/vault/credential-cache.ts (2)
libs/sdk/src/auth/vault/index.ts (2)
  • CREDENTIAL_CACHE (87-87)
  • CredentialCache (71-71)
libs/auth/src/vault/credential-cache.ts (1)
  • CredentialCache (24-241)
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (4)
libs/auth/src/vault/auth-providers.types.ts (2)
  • ResolvedCredential (71-84)
  • CredentialFactoryContext (93-112)
libs/auth/src/session/authorization-vault.ts (1)
  • Credential (247-247)
libs/sdk/src/auth/vault/auth-providers.registry.ts (1)
  • NormalizedProviderConfig (21-30)
libs/auth/src/vault/credential-helpers.ts (1)
  • extractCredentialExpiry (15-25)
libs/auth/src/authorization/authorization.types.ts (1)
libs/auth/src/authorization/index.ts (17)
  • AuthMode (9-9)
  • authModeSchema (24-24)
  • AuthUser (10-10)
  • authUserSchema (25-25)
  • AuthorizedTool (11-11)
  • authorizedToolSchema (26-26)
  • AuthorizedPrompt (12-12)
  • authorizedPromptSchema (27-27)
  • LLMSafeAuthContext (13-13)
  • llmSafeAuthContextSchema (28-28)
  • AppAuthState (20-20)
  • appAuthStateSchema (29-29)
  • AppAuthorizationRecord (14-14)
  • appAuthorizationRecordSchema (30-30)
  • ProgressiveAuthState (15-15)
  • progressiveAuthStateSchema (31-31)
  • AuthorizationCreateCtx (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: E2E Tests (demo-e2e-multiapp)
  • GitHub Check: E2E Tests (demo-e2e-config)
  • GitHub Check: E2E Tests (demo-e2e-openapi)
  • GitHub Check: E2E Tests (demo-e2e-hooks)
  • GitHub Check: E2E Tests (demo-e2e-cache)
  • GitHub Check: E2E Tests (demo-e2e-redis)
  • GitHub Check: E2E Tests (demo-e2e-public)
  • GitHub Check: E2E Tests (demo-e2e-serverless)
  • GitHub Check: E2E Tests (demo-e2e-notifications)
  • GitHub Check: E2E Tests (demo-e2e-transparent)
  • GitHub Check: E2E Tests (demo-e2e-errors)
  • GitHub Check: E2E Tests (demo-e2e-ui)
  • GitHub Check: E2E Tests (demo-e2e-codecall)
  • GitHub Check: E2E Tests (demo-e2e-providers)
  • GitHub Check: Unit Tests
🔇 Additional comments (32)
libs/sdk/src/auth/machine-id.ts (7)

17-18: Good use of @frontmcp/utils for utilities.

The imports correctly use @frontmcp/utils for randomUUID, mkdir, and writeFile, which aligns with coding guidelines for cross-platform support.


25-27: LGTM!

Simple and clear implementation for detecting development mode.


32-35: LGTM!

Path resolution logic handles both absolute and relative paths correctly.


72-89: LGTM - Fire-and-forget pattern is appropriate here.

Good use of @frontmcp/utils for mkdir and writeFile. The async IIFE with try-catch correctly handles errors without blocking startup, and the permission modes (0o700 for directory, 0o600 for file) are appropriately restrictive for storing sensitive identifiers.


103-123: Clear resolution priority with well-documented fallback chain.

The IIFE correctly implements the documented priority order (env var → file → random generation) and the fire-and-forget persistence maintains non-blocking startup.


125-131: LGTM!

Public API surface remains unchanged as noted in the summary.


47-66: The suggested fix referencing readFileSync from @frontmcp/utils is incorrect—that export does not exist.

The guideline to prefer @frontmcp/utils for file system operations is valid, but @frontmcp/utils exports only async file operations (readFile, writeFile, mkdir, etc.), not synchronous alternatives. The loadMachineIdSync() function requires synchronous I/O during bootstrap to load the machine ID before async initialization can proceed.

However, the type assertions in error handling should be replaced with proper type guards:

Use type guards for safer error handling
+function isNodeError(error: unknown): error is NodeJS.ErrnoException {
+  return error instanceof Error && 'code' in error;
+}
+
 try {
   // ... existing code ...
 } catch (error: unknown) {
   // ENOENT is expected on first run
-  if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
-    console.warn(`[MachineId] Failed to load from ${machineIdPath}: ${(error as Error).message}`);
+  if (isNodeError(error) && error.code === 'ENOENT') {
+    // Expected on first run, no warning needed
+  } else if (isNodeError(error)) {
+    console.warn(`[MachineId] Failed to load from ${machineIdPath}: ${error.message}`);
+  } else {
+    console.warn(`[MachineId] Failed to load from ${machineIdPath}: ${String(error)}`);
   }
   return null;
 }

For the synchronous file read itself, consider if the bootstrap initialization can be refactored to support async loading in a future update, or document this as an intentional exception to the file system utilities guideline for critical bootstrap paths.

libs/auth/src/vault/credential-helpers.ts (1)

15-25: LGTM - Clean utility function for credential expiry extraction.

The switch statement correctly handles credential types that have expiry timestamps and returns undefined for types without expiry semantics. The function is well-documented with clear JSDoc.

libs/auth/src/vault/credential-cache.ts (1)

24-31: LGTM - Well-designed cache implementation.

The CredentialCache class provides a clean API for session-scoped credential caching with appropriate TTL handling, eviction policy, and statistics tracking. The implementation is straightforward and suitable for the expected cache sizes.

libs/auth/src/vault/index.ts (1)

1-40: LGTM - Well-organized barrel exports.

The vault index consolidates types, schemas, helpers, and cache utilities following the project convention of exporting through barrel files. The grouping with comments improves discoverability.

libs/sdk/src/auth/authorization/authorization.types.ts (2)

3-27: LGTM - Clean separation of portable and SDK-specific types.

The file correctly re-exports portable types from @frontmcp/auth for downstream consumers while importing the same types locally for use in SDK-specific interfaces. This pattern maintains API consistency while allowing the SDK to extend the portable types.


38-135: Well-designed Authorization interface with comprehensive accessor methods.

The interface properly marks all properties as readonly and provides useful helper methods (hasScope, canAccessTool, etc.) that align with SDK usage patterns. The separation of ID arrays from record types (e.g., authorizedToolIds alongside authorizedTools) enables efficient lookups and iteration.

libs/auth/src/vault/auth-providers.types.ts (2)

188-202: Acceptable use of z.any() for function types with proper justification.

The comment on line 195 explains that functions are validated at runtime rather than through Zod, which is a valid approach since Zod cannot meaningfully validate function signatures. This justifies the z.any() usage per the coding guidelines requiring strong justification for any types.


56-62: Good use of .strict() on Zod schemas.

The schemas correctly apply .strict() to reject unknown properties, aligning with the coding guidelines for validation schemas.

libs/sdk/src/auth/vault/auth-providers.accessor.ts (2)

29-158: LGTM - Excellent interface design with comprehensive JSDoc.

The AuthProvidersAccessor interface provides a clean API for credential management with appropriate async/sync method signatures:

  • Async for operations that may trigger loading (get, has, listAvailable)
  • Sync for registry checks and cache invalidation (isRegistered, invalidate)

The JSDoc examples demonstrate practical usage patterns.


160-168: The DI token naming convention is consistent across the codebase. AUTH_PROVIDERS_ACCESSOR correctly follows the established Symbol.for('frontmcp:TOKEN_NAME') pattern used throughout the SDK for dependency injection tokens. No changes needed.

libs/auth/src/authorization/index.ts (1)

1-32: LGTM!

Clean barrel exports with proper separation of types, enums, and schemas. The use of export type for type-only exports is correct for optimal tree-shaking. Based on learnings, this follows the pattern of exporting all public APIs through appropriate entry points.

libs/sdk/src/auth/vault/credential-cache.ts (1)

1-14: LGTM!

Clean DI token definition following the established pattern. The use of Symbol.for() ensures consistent identity across module boundaries, and the type import from @frontmcp/auth maintains proper separation between the SDK's DI wiring and the auth library's implementation.

libs/sdk/src/auth/session/transport-session.manager.ts (2)

156-162: LGTM - Key derivation implementation.

The HKDF key derivation is correctly implemented with proper parameters: 32-byte output for AES-256, descriptive salt and info strings, and efficient reuse of the TextEncoder instance.


340-345: Verify the blob format compatibility.

The decryptAesGcmSync expects a SessionEncBlob with base64url-encoded strings, but the JWT format stored is ${iv}.${tag}.${ct} (line 313, 318). Ensure the decryption correctly handles this format - currently it passes the split parts directly which should work, but consider adding a comment documenting this format transformation for maintainability.

libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (1)

289-323: LGTM - Comprehensive header generation.

The defaultHeaders method properly handles all credential types with appropriate header formats. Good use of base64Encode from @frontmcp/utils as per coding guidelines.

libs/sdk/src/auth/vault/credential-loaders/index.ts (1)

8-9: Verify the intent of re-exporting from @frontmcp/auth.

Re-exporting extractCredentialExpiry from @frontmcp/auth creates a secondary import path. Consumers might import from either location, potentially causing confusion. If this is intentional for convenience within the credential-loaders context, consider adding a comment explaining the re-export. Otherwise, consumers should import directly from @frontmcp/auth.

libs/auth/src/session/session-crypto.ts (2)

36-62: LGTM - Async crypto functions.

The async encryptAesGcm and decryptAesGcm functions correctly use @frontmcp/utils primitives as per coding guidelines, with proper base64url encoding for the output blob.


73-75: LGTM - HKDF wrapper.

Clean passthrough wrapper for the @frontmcp/utils HKDF function.

libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (1)

1-162: LGTM on overall structure and implementation.

The class correctly implements lazy loading with in-flight deduplication, proper logging with duration tracking, and clean error propagation. The use of extractCredentialExpiry from @frontmcp/auth aligns with the library's centralized credential helpers. Based on learnings, the imports from @frontmcp/auth instead of internal paths is the correct approach for this refactored architecture.

libs/sdk/src/auth/vault/auth-providers.vault.ts (3)

8-12: Good use of specific MCP error class.

The import of InvalidInputError from the SDK's error module aligns with the coding guidelines requiring specific error classes with MCP error codes instead of generic errors. Based on learnings, this is the correct pattern for libs/sdk/**/*.ts files.


250-267: Robust exhaustive scope checking with proper error handling.

The buildVaultKey method correctly:

  1. Validates userId requirement for user-scoped credentials
  2. Uses the exhaustive never check pattern to ensure all CredentialScope values are handled
  3. Throws InvalidInputError with descriptive messages

This ensures compile-time safety when new scopes are added.


270-273: DI token correctly uses Symbol.for for cross-package compatibility.

Using Symbol.for('frontmcp:AUTH_PROVIDERS_VAULT') ensures the same symbol is used across different module instances, which is essential for DI to work correctly across package boundaries. The type assertion to Token<AuthProvidersVault> properly types the token.

libs/auth/src/session/index.ts (2)

67-69: Crypto exports align with centralized utility pattern.

The session-crypto exports (encryptAesGcm, decryptAesGcm, hkdfSha256, encryptAesGcmSync, decryptAesGcmSync) provide the recommended crypto utilities. Based on learnings, these should be used instead of node:crypto directly throughout the codebase.


1-96: Well-organized barrel exports with clear module groupings.

The file follows the guideline to export everything users need through barrel exports without backwards compatibility aliases. The exports are logically grouped by domain (Authorization Vault, Vault Encryption, Token Vault, etc.) with clear comments. The separation of runtime values (export { ... }) from types (export type { ... }) is consistent throughout.

libs/auth/src/index.ts (2)

1-32: Excellent documentation with practical usage example.

The module-level JSDoc provides clear context and a working example demonstrating three key use cases (JWKS, UI templates, credential vault). This helps developers quickly understand how to use the package.


34-260: Comprehensive public API surface with logical organization.

The barrel file successfully consolidates the @frontmcp/auth package's public API across all submodules (JWKS, UI, Session, Authorization, Utils, Vault). Each section is clearly demarcated with comments, and exports are complete based on the AI summary. This aligns with the guideline to export everything users need through barrel exports.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/sdk/src/auth/session/transport-session.manager.ts (1)

329-359: encodeSessionJwt method is not integrated into the session creation flow and appears to be incomplete/dead code.

While encodeSessionJwt (lines 290-320) encrypts additionalState.state and additionalState.tokens with encryptValue, it is never called in the codebase. The createSession method returns a TransportSession object directly without invoking encodeSessionJwt for stateless mode, and decryptSessionJwt never attempts to recover the inner-encrypted state/tokens fields.

The stateless session encoding/decoding path appears incomplete:

  • encodeSessionJwt is never invoked, so inner state/tokens are never stored in the JWT
  • decryptSessionJwt casts the decrypted payload as SessionJwtPayload (base type) rather than StatelessSessionJwtPayload, losing access to optional state and tokens fields even if they existed
  • TransportSession has no fields to carry these decrypted values

Either complete the stateless session implementation by integrating encodeSessionJwt into createSession and updating decryptSessionJwt to handle StatelessSessionJwtPayload, or remove the unused encryption code if stateless mode doesn't require carrying state/tokens in the session JWT.

🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/session/record/session.mcp.ts:
- Around line 13-14: The constructor in McpSession currently calls super(ctx as
any); remove the unsafe any cast by making the types compatible: either update
the base Session constructor signature to accept McpSessionCreateCtx (or a
common BaseCreateCtx that McpSessionCreateCtx extends), replace the cast with a
narrower type assertion only if structurally compatible (e.g., ctx as
BaseCreateCtx), or explicitly map ctx properties into the shape Session expects
before calling super; reference the constructor, McpSessionCreateCtx, and
Session when locating and applying the change.
- Around line 3-5: McpSessionCreateCtx currently declares apps: string[] but
that property is never propagated to BaseCreateCtx or stored, forcing an unsafe
ctx as any cast; either remove apps from McpSessionCreateCtx if MCP sessions
don't need it, or add a corresponding class property (e.g., this.apps) and/or
map it into the parent create context (BaseCreateCtx.authorizedAppIds) in the
constructor where McpSessionCreateCtx is used so the types align; then delete
the ctx as any cast and update signatures so McpSessionCreateCtx is assignable
to BaseCreateCtx without casting.

In @libs/sdk/src/auth/session/session.service.ts:
- Line 78: The session creation is ignoring the optional authorizedResources
from CreateSessionArgs by hardcoding authorizedResources: [], so update the
session builder in session.service.ts to use the passed-in value (e.g.,
args.authorizedResources ?? []) instead of an empty array; locate the code in
the create session function (where authorizedTools/authorizedPrompts are
handled) and replace the hardcoded array with args.authorizedResources ?? [] so
provided values are respected.
🧹 Nitpick comments (9)
libs/sdk/src/auth/session/session-crypto.ts (1)

44-60: Consider hardening the development fallback behavior.

The insecure default secret fallback (line 56) relies on NODE_ENV !== 'production' to determine safety. If NODE_ENV is unset or misconfigured in a production-like environment, the insecure default will be used silently after a warning.

Consider also checking for common production indicators like cloud environment variables or requiring explicit opt-in for the insecure default:

💡 Optional: More defensive environment check
   if (!secret) {
-    if (process.env['NODE_ENV'] === 'production') {
+    const isProduction = process.env['NODE_ENV'] === 'production' || 
+                         process.env['VERCEL_ENV'] === 'production' ||
+                         process.env['AWS_EXECUTION_ENV'];
+    if (isProduction) {
       throw new Error(
         '[SessionCrypto] MCP_SESSION_SECRET is required in production for session signing. ' +
           'Set this environment variable to a secure random string.',
       );
     }
libs/auth/src/index.ts (2)

128-136: Consider documenting the re-export rationale for crypto utilities.

Re-exporting hkdfSha256, encryptValue, decryptValue, encryptAesGcm, and decryptAesGcm from @frontmcp/utils creates a convenience API but also couples consumers to this package for crypto operations. This is fine if intentional, but a brief comment explaining why these are re-exported (e.g., "for use with vault encryption") would clarify the design intent.


42-47: Verify if dev-key-persistence exports should be marked as deprecated.

The exports isDevKeyPersistenceEnabled, resolveKeyPath, loadDevKey, saveDevKey, and deleteDevKey appear to be development-only utilities. Consider adding JSDoc @deprecated tags or grouping them under a separate comment indicating they're for development use only to prevent accidental production usage.

libs/sdk/src/auth/session/session.types.ts (2)

41-41: Replace any with unknown in claims type.

Per coding guidelines, use unknown instead of any for generic type parameter defaults. The claims property should use Record<string, unknown> for type safety.

♻️ Suggested fix
-  claims: Record<string, any>;
+  claims: Record<string, unknown>;

51-54: Replace any with unknown in details types.

The details properties in authorizedTools and authorizedPrompts should also use Record<string, unknown> instead of Record<string, any>.

♻️ Suggested fix
-  authorizedTools?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;
+  authorizedTools?: Record<string, { executionPath: [string, string]; details?: Record<string, unknown> }>;
   authorizedToolIds?: string[];
-  authorizedPrompts?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;
+  authorizedPrompts?: Record<string, { executionPath: [string, string]; details?: Record<string, unknown> }>;
libs/utils/src/crypto/hmac-signing.ts (2)

154-161: Consider strengthening the signed data detection.

The function only checks for the presence of sig and v properties. Objects that coincidentally have these properties could produce false positives. Consider also checking that v is a number (specifically 1 for the current version) and that data exists.

♻️ Optional: Stricter detection
 export function isSignedData(json: string): boolean {
   try {
     const parsed = JSON.parse(json);
-    return parsed && typeof parsed === 'object' && 'sig' in parsed && 'v' in parsed;
+    return (
+      parsed &&
+      typeof parsed === 'object' &&
+      'sig' in parsed &&
+      'v' in parsed &&
+      'data' in parsed &&
+      typeof parsed.sig === 'string' &&
+      parsed.v === 1
+    );
   } catch {
     return false;
   }
 }

171-183: Type assertion on unsigned data bypasses runtime validation.

The as T cast on line 179 is inherently unsafe since there's no schema validation for legacy unsigned data. The inline comment (line 177) is a good warning, but callers might miss it.

Consider adding a Zod schema overload or a generic validator parameter for scenarios where type safety matters:

export function verifyOrParseData<T>(
  json: string,
  config: HmacSigningConfig,
  schema?: z.ZodType<T>
): T | null;

This would allow callers to opt into validation for unsigned data during migration.

libs/sdk/src/auth/session/session.service.ts (2)

39-52: Reduce any type assertions.

Multiple as any casts here and on lines 74-75 work around type mismatches. Per coding guidelines, avoid any types without strong justification.

The claims parameter is already typed as Record<string, any> in CreateSessionArgs, so the cast on line 40 is redundant. For the provider snapshot casts, consider aligning the ProviderSnapshot type with the constructed object shape, or use a type assertion to the specific expected type rather than any.

♻️ Suggested improvement for claims access
     const expClaim =
-      args.claims && typeof (args.claims as any)['exp'] === 'number'
-        ? Number((args.claims as any)['exp'])
+      args.claims && typeof args.claims['exp'] === 'number'
+        ? Number(args.claims['exp'])
         : undefined;

65-85: Final as any cast bypasses McpSession type checking.

The constructor call ends with as any, hiding any type mismatches between the provided object and McpSessionCreateCtx. This makes it easy for bugs to slip through if the context type changes.

Consider defining the context object with explicit typing, or updating McpSessionCreateCtx to match the constructed shape. If the cast is unavoidable due to base class constraints (as suggested by McpSession using super(ctx as any)), document why.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b44d34d and 0470ddc.

📒 Files selected for processing (19)
  • libs/auth/src/index.ts
  • libs/auth/src/session/authorization.store.ts
  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.ts
  • libs/sdk/src/auth/instances/instance.local-primary-auth.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/record/session.mcp.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
  • libs/sdk/src/auth/session/record/session.transparent.ts
  • libs/sdk/src/auth/session/session-crypto.ts
  • libs/sdk/src/auth/session/session.schema.ts
  • libs/sdk/src/auth/session/session.service.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
  • libs/utils/src/crypto/hmac-signing.ts
  • libs/utils/src/crypto/index.ts
  • libs/utils/src/index.ts
💤 Files with no reviewable changes (3)
  • libs/sdk/src/auth/session/record/session.transparent.ts
  • libs/sdk/src/auth/session/record/session.stateless.ts
  • libs/sdk/src/auth/session/record/session.stateful.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/sdk/src/auth/instances/instance.local-primary-auth.ts
  • libs/sdk/src/auth/vault/auth-providers.vault.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.ts
  • libs/sdk/src/auth/session/session.schema.ts
  • libs/sdk/src/auth/session/record/session.mcp.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/utils/src/crypto/hmac-signing.ts
  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/session/session-crypto.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/session.service.ts
  • libs/utils/src/crypto/index.ts
  • libs/auth/src/index.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Include instanceof checks in tests for error classes to verify proper error hierarchy

Files:

  • libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.ts
  • libs/sdk/src/auth/session/session.schema.ts
  • libs/sdk/src/auth/session/record/session.mcp.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/sdk/src/auth/session/session-crypto.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.ts
  • libs/sdk/src/auth/session/session.schema.ts
  • libs/sdk/src/auth/session/record/session.mcp.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/sdk/src/auth/session/session-crypto.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.ts
  • libs/sdk/src/auth/session/session.schema.ts
  • libs/sdk/src/auth/session/record/session.mcp.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/utils/src/crypto/hmac-signing.ts
  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/session/session-crypto.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/session.service.ts
  • libs/utils/src/crypto/index.ts
  • libs/auth/src/index.ts
libs/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Files:

  • libs/auth/src/index.ts
🧠 Learnings (16)
📓 Common learnings
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods

Applied to files:

  • libs/sdk/src/auth/session/session.schema.ts
  • libs/sdk/src/auth/session/record/session.mcp.ts
  • libs/sdk/src/auth/session/session.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files

Applied to files:

  • libs/sdk/src/auth/session/session.schema.ts
  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Use `unknown` instead of `any` for generic type parameter defaults (not for MCP protocol types)

Applied to files:

  • libs/sdk/src/auth/session/session.schema.ts
  • libs/sdk/src/auth/session/session.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errors

Applied to files:

  • libs/sdk/src/auth/session/session.schema.ts
  • libs/sdk/src/auth/session/record/session.mcp.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Extend ExecutionContextBase with plugin-specific properties using module declaration (`declare module 'frontmcp/sdk'`) combined with `contextExtensions` in plugin metadata

Applied to files:

  • libs/sdk/src/auth/session/record/session.mcp.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/utils/src/crypto/hmac-signing.ts
  • libs/sdk/src/auth/session/session-crypto.ts
  • libs/utils/src/crypto/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/utils/src/crypto/hmac-signing.ts
  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/session/session-crypto.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/utils/src/crypto/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/utils/src/crypto/hmac-signing.ts
  • libs/sdk/src/auth/session/session-crypto.ts
  • libs/utils/src/crypto/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • libs/utils/src/crypto/hmac-signing.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Applied to files:

  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/utils/src/crypto/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/auth/src/session/index.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/utils/src/crypto/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/sdk/src/auth/session/index.ts
  • libs/auth/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality

Applied to files:

  • libs/auth/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/auth/src/index.ts
🧬 Code graph analysis (5)
libs/sdk/src/auth/session/session.schema.ts (1)
libs/sdk/src/auth/session/record/session.mcp.ts (1)
  • McpSession (11-20)
libs/sdk/src/auth/session/record/session.mcp.ts (1)
libs/sdk/src/auth/session/record/session.base.ts (1)
  • BaseCreateCtx (8-29)
libs/sdk/src/auth/session/transport-session.manager.ts (3)
libs/utils/src/crypto/index.ts (3)
  • hkdfSha256 (96-104)
  • encryptValue (270-270)
  • decryptValue (271-271)
libs/utils/src/index.ts (3)
  • hkdfSha256 (77-77)
  • encryptValue (94-94)
  • decryptValue (95-95)
libs/sdk/src/auth/session/transport-session.types.ts (1)
  • SessionJwtPayload (124-137)
libs/utils/src/crypto/hmac-signing.ts (2)
libs/utils/src/crypto/index.ts (8)
  • SignedData (332-332)
  • HmacSigningConfig (333-333)
  • hmacSha256 (81-83)
  • base64urlEncode (182-197)
  • base64urlDecode (202-220)
  • timingSafeEqual (154-159)
  • isSignedData (337-337)
  • verifyOrParseData (338-338)
libs/utils/src/index.ts (8)
  • SignedData (134-134)
  • HmacSigningConfig (135-135)
  • hmacSha256 (76-76)
  • base64urlEncode (82-82)
  • base64urlDecode (83-83)
  • timingSafeEqual (80-80)
  • isSignedData (138-138)
  • verifyOrParseData (139-139)
libs/sdk/src/auth/session/session.service.ts (4)
libs/sdk/src/auth/session/record/session.base.ts (1)
  • scope (106-108)
libs/sdk/src/scope/scope.instance.ts (1)
  • Scope (37-392)
libs/sdk/src/auth/session/session.types.ts (1)
  • CreateSessionArgs (38-55)
libs/sdk/src/auth/session/record/session.mcp.ts (1)
  • McpSession (11-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (19)
libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.ts (1)

15-15: LGTM! Import correctly migrated to the new package.

The import path update from local modules to @frontmcp/auth aligns with the PR's objective to consolidate authentication APIs into a unified package surface.

libs/sdk/src/auth/session/session-crypto.ts (2)

12-19: LGTM! Correctly uses @frontmcp/utils for cryptographic operations.

The migration to generic HMAC signing utilities from @frontmcp/utils aligns with the coding guidelines requiring use of @frontmcp/utils for cryptographic operations instead of node:crypto directly.


85-86: LGTM! Clean delegation to utility functions.

The session signing and verification functions correctly delegate to the generic utilities while providing session-specific typing and configuration resolution.

libs/auth/src/index.ts (1)

1-48: Well-structured barrel exports for the new package.

The module organization with clear section comments makes the API surface discoverable. The example in the JSDoc provides helpful guidance for consumers.

libs/sdk/src/auth/session/session.types.ts (1)

5-6: LGTM! SessionMode consolidation to 'mcp'.

The simplification from a three-variant union to a single 'mcp' literal aligns with the PR's objective to unify session handling under the MCP model.

libs/sdk/src/auth/session/session.schema.ts (1)

1-4: LGTM! Clean schema simplification.

The schema consolidation from a three-type union to a single McpSession instanceof check aligns with the broader MCP session unification. This simplifies validation logic and reduces maintenance burden.

libs/sdk/src/auth/session/index.ts (1)

24-44: LGTM! Clean re-export structure from @frontmcp/auth.

The migration to re-exporting authorization store components from @frontmcp/auth properly centralizes the auth API surface while maintaining backwards compatibility for SDK consumers. The separation between SDK-specific session utilities (transport, rate limiting, crypto) and the shared auth package components is well-structured.

libs/utils/src/crypto/hmac-signing.ts (4)

1-25: LGTM! Well-documented module with correct imports.

The module documentation is comprehensive with a clear example. Imports correctly use internal crypto utilities from ./index rather than node:crypto directly, following coding guidelines.


26-48: LGTM! Clean interface definitions with forward-compatible versioning.

The SignedData<T> interface with version field (v: 1) enables future algorithm changes without breaking existing data. The comment recommending at least 32 bytes for the secret is helpful guidance.


50-88: LGTM! Correct signing implementation.

The signature computation over the JSON-stringified data and the wrapper structure are sound. Using base64url encoding for the signature is appropriate for storage/transport.


109-145: LGTM! Robust verification with proper timing-safe comparison.

Good defensive implementation:

  • Validates structure before processing
  • Uses timing-safe comparison to prevent timing attacks
  • Length check before timingSafeEqual avoids the exception it would throw on mismatched lengths
  • Catches all exceptions and returns null for invalid inputs
libs/utils/src/crypto/index.ts (2)

222-249: LGTM! Standard base64 utilities complement existing base64url functions.

Clean implementation following the established cross-platform pattern. The JSDoc correctly references RFC 4648 Section 4 and notes the use case (HTTP Basic auth).


328-367: LGTM! Well-organized re-exports expand the crypto API surface.

The re-exports from ./hmac-signing and ./key-persistence are clearly sectioned with comments distinguishing types, schemas, classes, and factory functions. This makes the barrel export discoverable and maintainable.

libs/auth/src/session/index.ts (1)

1-125: LGTM! Comprehensive barrel export for the session module.

The file consolidates session-related exports into a single public entry point with clear section comments. Re-exporting crypto utilities from @frontmcp/utils (lines 90-98) centralizes dependencies for consumers of the auth package. This follows the guideline to export everything users need through barrel exports.

libs/sdk/src/auth/session/transport-session.manager.ts (5)

3-3: LGTM!

The imports correctly use @frontmcp/utils for cryptographic operations (encryptValue, decryptValue, hkdfSha256) as required by the coding guidelines.


122-122: LGTM!

The migration from Buffer to Uint8Array aligns with the broader refactor toward portable, Web Crypto-compatible primitives. Since encryptionKey is private readonly, this change has no external API impact.


154-160: LGTM!

The HKDF key derivation correctly uses TextEncoder for string-to-Uint8Array conversion before passing to hkdfSha256. The 32-byte output is appropriate for AES-256-GCM operations used downstream.


309-317: LGTM!

The stateless encoding correctly uses encryptValue from @frontmcp/utils and formats the encrypted output as iv.tag.data, which matches the expected format in decryptSessionJwt. The symmetric handling of both state and tokens is consistent.


337-341: > Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @libs/plugins/src/index.ts:
- Around line 11-12: Update the barrel file comment that currently notes
"ConfigPlugin is now built into @frontmcp/sdk" to include explicit migration
guidance: mention that the `@frontmcp/plugin-config` re-export has been removed
and instruct users to import `ConfigPlugin` (or equivalent) directly from
`@frontmcp/sdk` (e.g., "import { ConfigPlugin } from '@frontmcp/sdk'"), and add
a one-line pointer to the existing migration docs at
`docs/draft/docs/extensibility/config-yaml.mdx` for more details; ensure the
comment references the symbol name `ConfigPlugin`, the removed package
`@frontmcp/plugin-config`, and the new package `@frontmcp/sdk`.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0470ddc and 7a0f4ab.

📒 Files selected for processing (10)
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • libs/plugins/package.json
  • libs/plugins/src/index.ts
  • plugins/plugin-config/jest.config.ts
  • plugins/plugin-config/src/__tests__/config.plugin.test.ts
  • plugins/plugin-config/src/__tests__/config.service.test.ts
  • plugins/plugin-config/src/__tests__/env-loader.test.ts
  • plugins/plugin-config/src/index.ts
  • plugins/plugin-config/tsconfig.spec.json
  • tsconfig.base.json
💤 Files with no reviewable changes (2)
  • libs/plugins/package.json
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
🧰 Additional context used
📓 Path-based instructions (5)
libs/plugins/**/*.ts

📄 CodeRabbit inference engine (libs/plugins/CLAUDE.md)

libs/plugins/**/*.ts: Extend tool metadata using declare global pattern to allow tools to specify plugin-specific options in their decorators
Always use @frontmcp/utils for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of node:crypto
RememberPlugin automatically provides this.remember and this.approval on execution contexts when installed; use helper functions getRemember() or tryGetRemember() for graceful degradation
RememberPlugin scopes include: session (default, current session only), user (persists across sessions), tool (tied to specific tool + session), and global (shared across all sessions/users)
Configure approval requirements on tools using the approval decorator property with fields: required, defaultScope, category, riskLevel, and approvalMessage
Avoid using node:crypto directly; always use @frontmcp/utils for cross-platform cryptographic support
Use proper ES module imports instead of require() for SDK imports; avoid dynamic require of @frontmcp/sdk modules
Always use the override keyword when inheriting and overriding methods from parent classes
Avoid using any type without justification in TypeScript files
Never hardcode encryption keys; use environment variables for key management
Do not expose internal error details in public-facing error messages

Files:

  • libs/plugins/src/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/plugins/src/index.ts
libs/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Files:

  • libs/plugins/src/index.ts
libs/plugins/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use module augmentation and runtime prototype modification to extend ExecutionContextBase with plugin properties

Files:

  • libs/plugins/src/index.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/plugins/src/index.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Use module augmentation for context properties via `declare module 'frontmcp/sdk'` combined with runtime plugin metadata `contextExtensions`, not module-level side effects

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Extend ExecutionContextBase with plugin-specific properties using module declaration (`declare module 'frontmcp/sdk'`) combined with `contextExtensions` in plugin metadata

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Avoid module-level side effects for context extensions as they cause circular dependencies; install extensions in plugin constructor instead

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : RememberPlugin automatically provides `this.remember` and `this.approval` on execution contexts when installed; use helper functions `getRemember()` or `tryGetRemember()` for graceful degradation

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : RememberPlugin scopes include: `session` (default, current session only), `user` (persists across sessions), `tool` (tied to specific tool + session), and `global` (shared across all sessions/users)

Applied to files:

  • libs/plugins/src/index.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
libs/plugins/src/index.ts (1)

15-18: Exports structure is correct.

The barrel exports follow the guidelines—re-exporting from individual plugin packages without backward compatibility aliases.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/sdk/src/auth/session/transport-session.manager.ts (1)

309-323: Remove unused additionalState parameter or implement proper decryption flow.

The additionalState parameter in encodeSessionJwt is never passed by callers (line 206 only passes session). Additionally, even if it were passed, the double encryption at lines 313–319 would create encrypted state and tokens fields that are never decrypted—decryptSessionJwt only decrypts the outer encryptJson layer and discards these fields during the return mapping (lines 350–356).

Either remove this unused parameter and code path, or implement the complete decryption and retrieval flow for stateless sessions that include additional state.

🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/session/transport-session.manager.ts:
- Around line 340-344: The encryptJson implementation is using node:crypto
directly which violates project guidelines; replace its low-level AES-256-GCM
logic with the shared helper encryptValue from @frontmcp/utils so its output
matches decryptValue's expected base64url {alg,iv,tag,data} shape; update the
session-id.utils.ts import to pull in encryptValue and remove direct node:crypto
usage inside encryptJson, call encryptValue with the same encryptionKey and JSON
payload, and ensure the returned object still exposes iv, tag, and data in
base64url so decryptValue(payload, this.encryptionKey) continues to work
unchanged.
🧹 Nitpick comments (3)
libs/sdk/src/auth/session/session.service.ts (2)

11-13: Remove unnecessary async keyword.

createSession is marked async but createMcpSession returns synchronously. The async keyword adds unnecessary overhead by wrapping the result in a Promise.

♻️ Suggested fix
-  async createSession(scope: Scope, args: CreateSessionArgs) {
+  createSession(scope: Scope, args: CreateSessionArgs) {
     return this.createMcpSession(scope, args);
   }

26-31: Silent catch may hide legitimate errors.

The empty catch block silently swallows all errors from app.tools.getTools(), defaulting to an empty array. This could mask configuration issues or bugs in tool registration.

♻️ Consider logging the error
         try {
           const toolNames = app.tools.getTools().map((t) => String(t.metadata.name));
           authorizedApps[app.id] = { id: app.id, toolIds: toolNames };
-        } catch {
+        } catch (err) {
+          // Log at debug level to aid troubleshooting without breaking session creation
+          scope.logger?.debug?.('Failed to retrieve tools for app', { appId: app.id, error: err });
           authorizedApps[app.id] = { id: app.id, toolIds: [] };
         }
libs/sdk/src/auth/session/transport-session.manager.ts (1)

359-361: Consider adding debug-level logging for decryption failures.

The empty catch block silently returns null for any decryption error. While this is appropriate for security (avoiding information disclosure), it may make debugging difficult.

Consider adding a debug-level log when NODE_ENV !== 'production' to aid troubleshooting invalid session tokens during development.

💡 Optional: Add debug logging
-    } catch {
+    } catch (err) {
+      if (process.env['NODE_ENV'] !== 'production') {
+        console.debug('[TransportSessionManager] Failed to decrypt session JWT:', err);
+      }
       return null;
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a0f4ab and 12db15e.

📒 Files selected for processing (7)
  • .claude/skills/fix-pr/SKILL.md
  • libs/plugins/src/index.ts
  • libs/sdk/src/auth/session/record/session.base.ts
  • libs/sdk/src/auth/session/record/session.mcp.ts
  • libs/sdk/src/auth/session/session.service.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
✅ Files skipped from review due to trivial changes (1)
  • .claude/skills/fix-pr/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/sdk/src/auth/session/record/session.mcp.ts
🧰 Additional context used
📓 Path-based instructions (7)
libs/plugins/**/*.ts

📄 CodeRabbit inference engine (libs/plugins/CLAUDE.md)

libs/plugins/**/*.ts: Extend tool metadata using declare global pattern to allow tools to specify plugin-specific options in their decorators
Always use @frontmcp/utils for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of node:crypto
RememberPlugin automatically provides this.remember and this.approval on execution contexts when installed; use helper functions getRemember() or tryGetRemember() for graceful degradation
RememberPlugin scopes include: session (default, current session only), user (persists across sessions), tool (tied to specific tool + session), and global (shared across all sessions/users)
Configure approval requirements on tools using the approval decorator property with fields: required, defaultScope, category, riskLevel, and approvalMessage
Avoid using node:crypto directly; always use @frontmcp/utils for cross-platform cryptographic support
Use proper ES module imports instead of require() for SDK imports; avoid dynamic require of @frontmcp/sdk modules
Always use the override keyword when inheriting and overriding methods from parent classes
Avoid using any type without justification in TypeScript files
Never hardcode encryption keys; use environment variables for key management
Do not expose internal error details in public-facing error messages

Files:

  • libs/plugins/src/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/plugins/src/index.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/record/session.base.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Files:

  • libs/plugins/src/index.ts
libs/plugins/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use module augmentation and runtime prototype modification to extend ExecutionContextBase with plugin properties

Files:

  • libs/plugins/src/index.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/plugins/src/index.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/record/session.base.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/record/session.base.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/record/session.base.ts
  • libs/sdk/src/auth/session/session.types.ts
  • libs/sdk/src/auth/session/session.service.ts
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Use module augmentation for context properties via `declare module 'frontmcp/sdk'` combined with runtime plugin metadata `contextExtensions`, not module-level side effects

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Extend ExecutionContextBase with plugin-specific properties using module declaration (`declare module 'frontmcp/sdk'`) combined with `contextExtensions` in plugin metadata

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/plugins/src/index.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/plugins/src/index.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • libs/plugins/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Use `unknown` instead of `any` for generic type parameter defaults (not for MCP protocol types)

Applied to files:

  • libs/sdk/src/auth/session/record/session.base.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files

Applied to files:

  • libs/sdk/src/auth/session/record/session.base.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods

Applied to files:

  • libs/sdk/src/auth/session/session.types.ts
  • libs/sdk/src/auth/session/session.service.ts
🧬 Code graph analysis (2)
libs/sdk/src/auth/session/transport-session.manager.ts (3)
libs/utils/src/index.ts (3)
  • hkdfSha256 (77-77)
  • encryptValue (94-94)
  • decryptValue (95-95)
libs/utils/src/crypto/index.ts (3)
  • hkdfSha256 (96-104)
  • encryptValue (270-270)
  • decryptValue (271-271)
libs/sdk/src/auth/session/transport-session.types.ts (1)
  • SessionJwtPayload (124-137)
libs/sdk/src/auth/session/session.service.ts (3)
libs/sdk/src/scope/scope.instance.ts (1)
  • Scope (37-392)
libs/sdk/src/auth/session/session.types.ts (1)
  • CreateSessionArgs (38-55)
libs/sdk/src/auth/session/record/session.mcp.ts (1)
  • McpSession (7-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (14)
libs/plugins/src/index.ts (2)

18-21: LGTM!

The barrel exports correctly re-export the remaining official plugins using proper ES module syntax. The removal of @frontmcp/plugin-config aligns with the migration to @frontmcp/sdk.


11-15: Migration note is clear and well-documented.

The deprecation guidance for ConfigPlugin is properly communicated, and the referenced documentation at docs/draft/docs/extensibility/config-yaml.mdx exists and comprehensively covers the migration path. Users moving from @frontmcp/plugins to @frontmcp/sdk will have clear examples and configuration guidance. The code change and documentation are properly aligned.

libs/sdk/src/auth/session/record/session.base.ts (5)

25-28: LGTM! Type safety improvement aligns with guidelines.

Changing details from Record<string, any> to Record<string, unknown> enforces proper type narrowing when accessing these values. Based on learnings, this correctly applies unknown for generic type defaults.


40-42: LGTM! Proper typing for extensible claims.

Using unknown for the index signature enforces type narrowing at access sites, which is the correct pattern for JWT claims where values can be of various types.


61-63: LGTM! Clean removal of type assertion with proper type guard.

The exp claim access now correctly handles the unknown type via the typeof check instead of using an unsafe as any cast. The property type changes maintain consistency with BaseCreateCtx.

Also applies to: 82-85


141-143: LGTM!

Formatting improvements for readability with no behavioral changes.

Also applies to: 148-151


169-175: Verify: transportId getter returns method reference, not result.

While reviewing the adjacent SessionView class, I noticed that transportId returns this.parent.getTransportSessionId (a method reference) rather than invoking it. This appears to be a pre-existing issue, but given the refactoring context, verify whether this is intentional or should be:

get transportId() {
  return this.parent.getTransportSessionId();
}

Or if async handling is needed:

getTransportId() {
  return this.parent.getTransportSessionId();
}
libs/sdk/src/auth/session/session.types.ts (2)

37-54: Type tightening from any to unknown is correct.

The changes to use Record<string, unknown> for claims, authorizedTools.details, and authorizedPrompts.details align with the coding guidelines requiring unknown instead of any for generic type parameter defaults. This enforces proper type narrowing at consumption sites.


5-6: Address the SessionMode type fragmentation across the SDK.

The original concern about downstream consumers is overstated—only session.base.ts directly imports the narrowed SessionMode = 'mcp' definition. However, there's a more serious issue: SessionMode is defined in three separate locations with conflicting values:

  • libs/sdk/src/auth/session/session.types.ts:6'mcp'
  • libs/sdk/src/common/types/options/session.options.ts:5'stateful' | 'stateless'
  • libs/sdk/src/errors/authorization-required.error.ts:24'stateful' | 'stateless'

Per the coding guidelines ("Centralize record types in common/records and import from there"), consolidate these definitions into a single canonical location.

libs/sdk/src/auth/session/session.service.ts (1)

63-81: MCP session construction looks correct.

The McpSession instantiation properly passes all required fields from args and computed values. The fallback patterns for authorizedResources (line 75) and scopes resolution are sensible defaults.

libs/sdk/src/auth/session/transport-session.manager.ts (4)

3-3: LGTM!

The import consolidation from @frontmcp/utils correctly follows the project's coding guidelines. Based on learnings, using @frontmcp/utils for cryptographic operations instead of node:crypto directly is the expected pattern.


122-122: LGTM!

The type change from Buffer to Uint8Array correctly aligns with the hkdfSha256 return type and modern crypto APIs. Since the property is private readonly, this is not a breaking change.


154-160: LGTM!

The HKDF key derivation correctly uses TextEncoder to convert strings to Uint8Array for cross-platform compatibility. The 32-byte key length is appropriate for AES-256-GCM encryption.


204-207: LGTM!

The stateless mode correctly encodes the session as an encrypted JWT, using the JWT string itself as the session ID. This enables stateless session handling where the client carries the encrypted session state.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/sdk/src/auth/session/transport-session.manager.ts (1)

121-160: Fix key derivation mismatch between encryptJson and decryptSessionJwt.

In stateless mode, sessions are encrypted via encodeSessionJwtencryptJson, which uses getKey() (SHA256-derived key), but decrypted via decryptSessionJwt, which uses this.encryptionKey (HKDF-derived key). These keys are different despite the same source material, causing all stateless session round-trips to fail. Either align both to use HKDF derivation with the same context/salts, or pass this.encryptionKey to encryptJson so it uses the same key for encryption and decryption.

🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/session/session.service.ts:
- Around line 35-50: The code recomputes both authorizedProviders and
authorizedProviderIds whenever either is missing and builds authorizedAppIds
from appIds (scope.apps.getApps()) ignoring args.authorizedAppIds, causing
mismatches; change the logic so you only derive the missing piece: if
authorizedProviderIds is missing derive it from Object.keys(authorizedProviders)
and if authorizedProviders is missing build it from primary/claims as currently
done, and for apps prefer args.authorizedAppIds when present (authorizedAppIds
??= args.authorizedAppIds ?? appIds) and likewise prefer args.authorizedApps for
authorizedApps, falling back to scope.apps.getApps() only when caller values are
absent; ensure the created provider snapshot still uses primary.id, exp from
args.claims, payload args.claims, and apps built from the chosen
authorizedApps/authorizedAppIds.

In @libs/sdk/src/auth/session/transport-session.manager.ts:
- Around line 203-206: decryptSessionJwt() currently sets and returns session.id
= payload.sid (the decrypted UUID) which mismatches createSession() that uses
the JWT token as session.id; change decryptSessionJwt() to return id = the
original JWT token (the encoded string input), not payload.sid, so getSession()
returns the token form callers expect (ensure the function that takes the token
as input—decryptSessionJwt()/getSession()/sessionExists()—preserves and returns
the raw JWT as session.id rather than the decrypted sid).

In @libs/sdk/src/auth/session/utils/session-id.utils.ts:
- Around line 13-23: The getKey function currently falls back to getMachineId()
when MCP_SESSION_SECRET is unset, which is insecure; update getKey to require
MCP_SESSION_SECRET in non-development/test environments and fail fast (throw an
Error) or at minimum log a clear warning via the module logger when
MCP_SESSION_SECRET is missing, only allowing getMachineId() as a fallback for
explicit dev/test modes (detect via NODE_ENV or an isDev/isTest helper).
Reference the cachedKey variable and getKey/getMachineId functions: on first
call, if process.env['MCP_SESSION_SECRET'] is falsy and NODE_ENV is not
'development'/'test', throw an error stating the missing MCP_SESSION_SECRET and
how to set it; keep the existing sha256(TextEncoder().encode(...)) derivation
when the secret is present and still cache to cachedKey. Also add a short
comment and ensure any thrown error message documents the requirement so callers
fail fast.
🧹 Nitpick comments (2)
libs/sdk/src/auth/session/transport-session.manager.ts (1)

291-302: Public API change: encodeSessionJwt() signature change needs call-site + docs alignment.
This is a publishable SDK (libs/**), and encodeSessionJwt is a public method; removing the optional additionalState parameter is a breaking change unless coordinated.

Safer API shape (if you need to preserve backward compatibility)
-  encodeSessionJwt(session: TransportSession): string {
+  encodeSessionJwt(session: TransportSession, _additionalState?: unknown): string {
     const payload: SessionJwtPayload = {
       sid: session.id,
       aid: session.authorizationId,
       proto: session.protocol,
       nid: session.nodeId,
       iat: Math.floor(Date.now() / 1000),
       exp: session.expiresAt ? Math.floor(session.expiresAt / 1000) : undefined,
     };

     return encryptJson(payload);
   }
libs/sdk/src/auth/session/session.service.ts (1)

8-16: Add explicit return type to createMcpSession() to keep strictness.

Right now createMcpSession relies on inference; since this is a core SDK surface, it’s worth pinning it to McpSession explicitly.

Proposed change
   createSession(scope: Scope, args: CreateSessionArgs): McpSession {
     return this.createMcpSession(scope, args);
   }

-  private createMcpSession(scope: Scope, args: CreateSessionArgs) {
+  private createMcpSession(scope: Scope, args: CreateSessionArgs): McpSession {
     const primary = scope.auth;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12db15e and 848eb9c.

📒 Files selected for processing (4)
  • libs/sdk/src/auth/session/record/session.base.ts
  • libs/sdk/src/auth/session/session.service.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/utils/session-id.utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/sdk/src/auth/session/record/session.base.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/session.service.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/session.service.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/session.service.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/session.service.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods

Applied to files:

  • libs/sdk/src/auth/session/session.service.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/auth/session/transport-session.manager.ts
🧬 Code graph analysis (3)
libs/sdk/src/auth/session/utils/session-id.utils.ts (3)
libs/sdk/src/auth/session/encrypted-authorization-vault.ts (1)
  • getKey (143-150)
libs/sdk/src/auth/authorization/authorization.class.ts (1)
  • getMachineId (19-19)
libs/ui/src/bundler/index.ts (1)
  • sha256 (147-147)
libs/sdk/src/auth/session/session.service.ts (2)
libs/sdk/src/auth/session/session.types.ts (1)
  • CreateSessionArgs (38-55)
libs/sdk/src/auth/session/record/session.mcp.ts (1)
  • McpSession (7-16)
libs/sdk/src/auth/session/transport-session.manager.ts (3)
libs/utils/src/crypto/index.ts (2)
  • hkdfSha256 (96-104)
  • decryptValue (271-271)
libs/utils/src/index.ts (2)
  • hkdfSha256 (77-77)
  • decryptValue (95-95)
libs/sdk/src/auth/session/transport-session.types.ts (2)
  • TransportSession (21-45)
  • SessionJwtPayload (124-137)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Build Libraries
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
libs/sdk/src/auth/session/transport-session.manager.ts (2)

3-3: Good: crypto migrated to @frontmcp/utils (matches repo guidelines).
This aligns with the “don’t use node:crypto directly” guidance. As per coding guidelines.


319-341: Good: non-prod-only debug logging on decrypt failures (avoid leaking in prod).
Looks reasonable; just ensure err can’t include sensitive plaintext (it shouldn’t if decrypt fails before parse).

libs/sdk/src/auth/session/utils/session-id.utils.ts (2)

2-2: Good: crypto moved to @frontmcp/utils instead of node:crypto. Based on learnings, this is the right direction for cross-platform crypto support.


25-30: No issues found with encryption/decryption contract usage.

encryptValue() returns { alg, iv, tag, data } with all fields as base64url-encoded strings, and decryptValue() is synchronous and returns the decrypted payload directly—both assumptions in lines 25–30 and 36–45 are correct. The code properly constructs the dot-separated transport format in encryptJson() and correctly reconstructs the blob object in decryptSessionJson().

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/sdk/src/auth/session/session.service.ts (1)

31-37: Silent catch block may hide errors.

The catch block silently swallows all errors when retrieving tool names. This could mask genuine issues like configuration problems or type errors. Consider at minimum logging a warning, or narrowing the catch to expected error types.

Suggested improvement
         try {
           const toolNames = app.tools.getTools().map((t) => String(t.metadata.name));
           authorizedApps[app.id] = { id: app.id, toolIds: toolNames };
         } catch {
+          // Log for debugging in case of unexpected issues during app registration
+          console.warn(`[SessionService] Failed to retrieve tools for app ${app.id}, defaulting to empty toolIds`);
           authorizedApps[app.id] = { id: app.id, toolIds: [] };
         }
🧹 Nitpick comments (4)
libs/sdk/src/auth/session/utils/session-id.utils.ts (3)

165-179: Remove dead commented-out code.

This commented block references undefined symbols (MACHINE_ID, decoded, headerSse) and appears to be leftover from a previous implementation. Dead code clutters the codebase and can cause confusion.

🧹 Remove dead code
   return undefined;
-  // // Create fresh
-
-  // const decodedSse: SessionIdPayload = {
-  //   nodeId: MACHINE_ID,
-  //   authSig: currentAuthSig,
-  //   uuid: randomUUID(),
-  //   iat: nowSec(),
-  // };
-  // const header = encryptJson(decoded);
-  // const headerSse = encryptJson(decodedSse);
-  // cache.set(header, decoded);
-  // cache.set(headerSse, decodedSse);
-  // return { header, decoded, headerSse, isNew: true };
 }

246-254: Self-referential validation is confusing.

Line 247 passes the payload's own authSig as the expected signature to isValidSessionPayload. This means the signature check (d['authSig'] === sig) will trivially pass if the structure is correct.

If the intent is structural validation only, consider extracting a separate hasValidSessionStructure() function for clarity. If signature verification is intended, this needs an external reference.

♻️ Consider clarifying the intent
+function hasValidSessionStructure(dec: unknown): dec is SessionIdPayload {
+  if (typeof dec !== 'object' || dec === null) return false;
+  const d = dec as Record<string, unknown>;
+  return (
+    typeof d['nodeId'] === 'string' &&
+    typeof d['authSig'] === 'string' &&
+    typeof d['uuid'] === 'string' &&
+    typeof d['iat'] === 'number'
+  );
+}

 // In updateSessionPayload:
   const decrypted = safeDecrypt(sessionId);
-  if (
-    isValidSessionPayload(decrypted, (decrypted as SessionIdPayload)?.authSig || '') ||
-    isValidPublicSessionPayload(decrypted)
-  ) {
+  if (hasValidSessionStructure(decrypted) || isValidPublicSessionPayload(decrypted)) {

214-217: Consider adding Secure flag for production.

The cookie lacks the Secure flag, meaning it will be transmitted over HTTP. For production HTTPS deployments, this should be Secure to prevent session hijacking over unencrypted connections.

🔒 Add secure flag option
-export function generateSessionCookie(sessionId: string, ttlInMinutes = 60 * 24): string {
+export function generateSessionCookie(
+  sessionId: string,
+  ttlInMinutes = 60 * 24,
+  secure = process.env['NODE_ENV'] === 'production',
+): string {
   const expires = new Date(Date.now() + ttlInMinutes * 60 * 1000).toUTCString();
-  return `mcp_session_id=${sessionId}; Path=/; Expires=${expires}; HttpOnly; SameSite=Lax`;
+  const secureFlag = secure ? '; Secure' : '';
+  return `mcp_session_id=${sessionId}; Path=/; Expires=${expires}; HttpOnly; SameSite=Lax${secureFlag}`;
 }
libs/sdk/src/auth/session/session.service.ts (1)

78-81: Clarify the distinction between id and sessionId.

Using args.token as the id while also passing a separate sessionId may cause confusion. Consider adding a brief comment explaining the intent, or using a more descriptive identifier construction if id serves a different purpose than sessionId.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 848eb9c and ab2b614.

📒 Files selected for processing (3)
  • libs/sdk/src/auth/session/session.service.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/utils/session-id.utils.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
  • libs/sdk/src/auth/session/session.service.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods

Applied to files:

  • libs/sdk/src/auth/session/session.service.ts
🧬 Code graph analysis (2)
libs/sdk/src/auth/session/utils/session-id.utils.ts (3)
libs/sdk/src/auth/session/encrypted-authorization-vault.ts (1)
  • getKey (143-150)
libs/sdk/src/auth/authorization/authorization.class.ts (1)
  • getMachineId (19-19)
libs/ui/src/bundler/index.ts (1)
  • sha256 (147-147)
libs/sdk/src/auth/session/session.service.ts (3)
libs/sdk/src/scope/scope.instance.ts (2)
  • Scope (37-392)
  • apps (329-331)
libs/sdk/src/auth/session/session.types.ts (1)
  • CreateSessionArgs (38-55)
libs/sdk/src/auth/session/record/session.mcp.ts (1)
  • McpSession (7-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (16)
libs/sdk/src/auth/session/utils/session-id.utils.ts (6)

1-8: LGTM! Crypto imports correctly use @frontmcp/utils.

The migration from direct node:crypto usage to @frontmcp/utils aligns with the coding guidelines and learnings for cross-platform cryptographic support.


13-49: Good security posture with production enforcement.

The key derivation logic correctly:

  • Enforces MCP_SESSION_SECRET in production (fail-fast)
  • Provides appropriate warnings for non-production fallback
  • Uses sha256 for 32-byte key derivation (AES-256 compatible)

Consider exposing a clearCachedKey() function for test isolation if not already available elsewhere.


51-56: LGTM!

Clean usage of encryptValue with consistent output format matching the decryption expectations.


62-71: LGTM!

The validation correctly handles malformed input (e.g., "iv..data" where segments could be empty strings). The decryptValue call properly specifies the algorithm.


188-212: LGTM!

Session creation correctly uses getMachineId() and randomUUID() from the appropriate modules. Platform detection handling is sensible.


73-133: LGTM!

The validation type guards and decryption helpers are well-structured with appropriate error handling via safeDecrypt.

libs/sdk/src/auth/session/transport-session.manager.ts (6)

3-3: Good alignment with project crypto standards.

Using @frontmcp/utils for cryptographic operations (decryptValue, encryptValue, hkdfSha256) follows the project's cross-platform cryptographic conventions. Based on learnings, this is the required approach instead of node:crypto directly.


120-120: LGTM!

The encryptionKey type change from Buffer to Uint8Array aligns with the hkdfSha256 return type from @frontmcp/utils. Using TextEncoder for encoding inputs is the modern, cross-platform approach.

Also applies to: 152-158


202-205: Stateless session ID design is consistent.

The pattern where session.id becomes the encrypted JWT in stateless mode is correctly implemented. The original UUID stored in payload.sid serves as a unique identifier within the encrypted payload, while the JWT itself becomes the externally-visible session ID. This ensures round-trip consistency between createSession and decryptSessionJwt.


315-349: LGTM - Decryption implementation is correct.

The decryptSessionJwt method correctly:

  • Parses the iv.tag.data format matching encodeSessionJwt output
  • Returns jwt as session.id maintaining stateless mode consistency (well documented in comments)
  • Handles errors gracefully by returning null without exposing sensitive details
  • Only logs debug info in non-production environments (good security practice)

136-151: Good secret management with appropriate production safeguards.

The secret handling correctly:

  • Requires MCP_SESSION_SECRET or explicit encryptionSecret in production (fails fast)
  • Warns clearly in development when falling back to machine ID
  • Avoids hardcoded secrets (per coding guidelines)

This is a sensible approach for balancing development convenience with production security.


291-304: No action needed. The encryption algorithm is consistent.

The encryptValue function from @frontmcp/utils always uses AES-256-GCM by default (hardcoded as alg: 'A256GCM' at line 66 of encrypted-blob.ts); it is not configurable. The encodeSessionJwt method omits the algorithm field from the JWT string because it is redundant, and decryptSessionJwt correctly reconstructs the EncryptedBlob with alg: 'A256GCM'. Decryption will not fail—the algorithms match.

libs/sdk/src/auth/session/session.service.ts (4)

1-4: LGTM!

Imports are clean and appropriate for the refactored MCP-centric session creation flow.


6-13: Clean API simplification.

The consolidation to a single McpSession return type is a good architectural decision. The delegation pattern keeps the public API clean while allowing internal flexibility.


67-76: LGTM!

Good defensive handling of scope claims with support for both scope and scp claim names, and proper handling of array vs. space/comma-delimited string formats.


40-65: LGTM!

The provider snapshot derivation logic correctly handles all cases: using caller-provided values when available, deriving authorizedProviderIds from authorizedProviders keys when needed, and building a default snapshot from the primary auth provider as a fallback.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
libs/sdk/src/auth/session/session.service.ts (2)

32-37: Potential issue with String(t.metadata.name) if name is undefined.

If t.metadata is present but name is undefined, String(t.metadata.name) will produce the literal string "undefined" which could lead to phantom tool IDs. Consider adding a guard or filtering:

-          const toolNames = app.tools.getTools().map((t) => String(t.metadata.name));
+          const toolNames = app.tools.getTools()
+            .filter((t) => t.metadata?.name != null)
+            .map((t) => String(t.metadata.name));

Additionally, console.warn in library code can pollute consumer logs. Consider using an injectable logger or suppressing this in production builds.


43-44: Consider importing ProviderSnapshot at the top of the file.

The inline import('./session.types').ProviderSnapshot syntax works but reduces readability. Standard practice is to import types at the top of the file.

 import { Scope } from '../../scope';
-import { CreateSessionArgs } from './session.types';
+import { CreateSessionArgs, ProviderSnapshot } from './session.types';
 import { McpSession } from './record/session.mcp';

Then use ProviderSnapshot directly in the code.

Also applies to: 55-56

libs/sdk/src/auth/session/utils/session-id.utils.ts (1)

125-133: Redundant type casts after type guard.

After isValidPublicSessionPayload(dec) returns true, TypeScript narrows dec to SessionIdPayload. The explicit casts are unnecessary.

Suggested simplification
   const dec = safeDecrypt(sessionId);
   if (isValidPublicSessionPayload(dec)) {
     // Cache the decrypted payload for future requests
-    cache.set(sessionId, dec as SessionIdPayload);
-    return dec as SessionIdPayload;
+    cache.set(sessionId, dec);
+    return dec;
   }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab2b614 and 11b66b9.

📒 Files selected for processing (2)
  • libs/sdk/src/auth/session/session.service.ts
  • libs/sdk/src/auth/session/utils/session-id.utils.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/session.service.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/auth/session/session.service.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods

Applied to files:

  • libs/sdk/src/auth/session/session.service.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (12)
libs/sdk/src/auth/session/session.service.ts (4)

6-13: LGTM!

The public createSession method has a properly typed return of McpSession, and the clean delegation to the private method is appropriate for the refactored single-path architecture.


60-60: Verify hardcoded embedMode: 'plain' is intentional.

When building the default providerSnapshot, embedMode is hardcoded to 'plain'. Ensure this is the correct default for all MCP session scenarios, or consider deriving it from context/args if it can vary.


69-78: LGTM!

The scope resolution logic correctly handles the common JWT conventions where scopes can appear as either a space-delimited string (scope claim), comma-delimited string, or array (scp claim).


80-86: Verify using args.token as the session id is intentional.

The session id is set to args.token (Line 81), while sessionId is a separate field (Line 82). If token is a JWT, using the full token as an ID could:

  1. Result in very long IDs (JWTs can be 1KB+)
  2. Include sensitive claims if logged or stored

If this is intentional for lookup purposes, consider documenting the reasoning. Otherwise, consider using sessionId or a hash of the token as the ID.

libs/sdk/src/auth/session/utils/session-id.utils.ts (8)

1-8: LGTM!

Crypto operations correctly use @frontmcp/utils instead of node:crypto directly, as per coding guidelines.


25-49: Approve with minor verification note: NODE_ENV case-sensitivity.

The key derivation logic and production guardrails are well-implemented. One minor note: the NODE_ENV === 'production' check is case-sensitive. If NODE_ENV is inadvertently set to 'Production' or 'PRODUCTION', the machine ID fallback would be used. This follows standard conventions, but consider a case-insensitive check for defense-in-depth.

Optional: Case-insensitive NODE_ENV check
-  if (nodeEnv === 'production') {
+  if (nodeEnv?.toLowerCase() === 'production') {

51-71: LGTM!

Encryption and decryption correctly use @frontmcp/utils primitives with proper format handling. The caller (safeDecrypt) appropriately handles exceptions.


73-106: LGTM!

Good separation of concerns between structural validation (hasValidSessionStructure) and signature verification (isValidSessionPayload). The type guard pattern is correctly implemented.


155-177: LGTM!

The validation flow correctly checks cache first before decryption, and properly returns undefined on mismatch or failure, allowing callers to decide whether to create a new session.


186-210: LGTM!

Session creation correctly uses @frontmcp/utils for UUID generation and properly handles platform detection with sensible fallback behavior.


232-252: LGTM!

The update logic correctly handles both regular and public sessions. The Object.assign mutation and re-caching to refresh TTL is appropriate for this use case.


212-215: This function is not exported from the SDK public API and is not used anywhere in the codebase.

The generateSessionCookie function appears to be unused internal code. The SDK provides a proper HttpCookie interface (with secure boolean support) and serializeCookie function in server.validation.ts for handling cookies through the standard response mechanism. If session cookies need to be set, developers should use the HttpCookie object approach instead of raw cookie strings.

Consider removing this dead code or, if it serves a documented purpose, exporting it with proper security controls (including the Secure flag for production environments).

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In @apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts:
- Around line 38-39: The comment above the import line is misleading: the
symbols FrontMcp, Tool, ToolContext, Resource, ResourceContext, Prompt,
PromptContext, and App are SDK decorators and context types, not auth type
re-exports; update the comment to accurately describe these as "SDK decorators
and context/context types" (or similar) so it reflects the actual imports, e.g.,
replace "should re-export auth types" with a short note that these are SDK
decorators and context types.

In @apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts:
- Around line 125-141: The test inside the "JWKS Endpoint Compatibility" suite
currently allows non-200 responses to pass silently and asserts jwks.keys.length
>= 0 which is meaningless; update the test (the test titled 'should serve JWKS
endpoint for token verification') to explicitly assert that response.status ===
200 (or call test.skip conditionally if some configurations legitimately don't
expose JWKS) and then, after parsing JSON, assert that jwks.keys is an array and
that jwks.keys.length >= 1 to ensure at least one signing key is present; keep
the failing assertions explicit so regressions are caught.
- Around line 143-160: The test "should serve OAuth authorization server
metadata" inside the "Authorization Metadata Compatibility" describe block
currently treats 404 as an acceptable status in the expect([...]) check; remove
404 from that array so the test fails when the endpoint is missing, or instead
split into two explicit tests: one that asserts the endpoint exists (expect
status in [200,301,302,307,308]) for configurations that must expose
/.well-known/oauth-authorization-server, and a second conditional test (or one
guarded by a config/env flag) that documents and asserts 404 for configurations
that intentionally do not expose it; update the expect call(s) and test names
accordingly (referencing the existing test function and its use of response and
metadata parsing).

In @apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts:
- Around line 126-134: The test name "should provide helpful error messages for
missing required providers" is misleading because the body only calls
mcp.tools.list() and asserts tools contain 'get-app-info' and
'get-request-info'; either rename the test to reflect its behavior (e.g.,
"should list all expected tools when providers are configured") or modify the
test to actually trigger and assert error messages for missing providers by
simulating/omitting a required provider and verifying the error text; update the
test title and/or add assertions around the error scenario involving
mcp.tools.list() or the specific provider methods to match the chosen approach.

In @apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts:
- Around line 88-106: The test 'should delete a vault entry' fails to assert
success on the create call before parsing JSON; add
expect(createResult).toBeSuccessful() immediately after obtaining createResult
(before calling createResult.json<{ entryId: string }>()) so you only parse when
the response succeeded; locate the createResult variable in the test and insert
the assertion there (and apply the same pattern for any other tests that call
.json() like this).
- Around line 63-86: Before calling createResult.json() after
mcp.tools.call('create-vault-entry'), assert the createResult indicates success
(e.g., call the test helper like expect(createResult).toBeSuccessful() or
equivalent) so you only parse JSON on a successful response; update the test
around the create step (the createResult and createResult.json calls) to include
this success assertion and fail early with a clear message if creation did not
succeed.

In @apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts:
- Around line 56-63: In the credentials mapping inside the mapped =
credentials.map(...) expression, the output field createdAt is incorrectly
sourced from c.createdAt; update the mapping to use c.acquiredAt instead (i.e.,
set createdAt to c.acquiredAt) so the produced objects match the AppCredential
schema from @frontmcp/auth and pass validation.
🧹 Nitpick comments (24)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (2)

30-36: Assertion doesn't verify actual singleton behavior.

The comment states "Both calls should return the same instance data" but the assertions only verify that both have "providerScope":"GLOBAL". This doesn't prove they're the same instance. Consider extracting and comparing instanceId similar to the Provider Lifecycle test.

♻️ Suggested improvement
       // Both calls should return the same instance data
       const content1 = JSON.stringify(result1);
       const content2 = JSON.stringify(result2);
 
-      // Verify GLOBAL scope behavior
+      // Verify GLOBAL scope and singleton behavior
       expect(content1).toContain('"providerScope":"GLOBAL"');
       expect(content2).toContain('"providerScope":"GLOBAL"');
+
+      // Verify same instance is returned (singleton)
+      const match1 = content1.match(/"instanceId":"([^"]+)"/);
+      const match2 = content2.match(/"instanceId":"([^"]+)"/);
+      expect(match1?.[1]).toBe(match2?.[1]);

145-151: Silent pass when content type is not 'text'.

The conditional at line 146 means if message.content.type is not 'text', the test passes without verifying the multi-provider content. Consider adding an assertion to ensure the expected content type is present, or fail explicitly if it's not.

♻️ Suggested fix
       const message = result.messages[0];
-      if (message.content.type === 'text') {
-        // Prompt accesses both GLOBAL and CONTEXT providers
-        expect(message.content.text).toContain('GLOBAL Scope Provider');
-        expect(message.content.text).toContain('CONTEXT Scope Provider');
-      }
+      expect(message.content.type).toBe('text');
+      if (message.content.type === 'text') {
+        // Prompt accesses both GLOBAL and CONTEXT providers
+        expect(message.content.text).toContain('GLOBAL Scope Provider');
+        expect(message.content.text).toContain('CONTEXT Scope Provider');
+      }
apps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.ts (4)

27-39: Consider simplifying the OAuth server initialization pattern.

The current approach starts the server, immediately stops it to capture the port, then recreates everything with the actual values. This adds unnecessary latency to test setup.

If MockOAuthServer supports a getNextAvailablePort() static method or allows port 0 with subsequent resolution, the setup could be consolidated into a single start cycle.


170-189: Strengthen assertions to verify actual claim values.

The test is named "should extract standard claims" but only asserts that client.auth is defined. To properly verify backward compatibility of claims extraction, consider asserting the actual claim values:

      expect(client.isConnected()).toBe(true);
      // Auth info should be available (client stores it from connection)
      expect(client.auth).toBeDefined();
+     expect(client.auth.sub).toBe('user-claims-test');
+     expect(client.auth.email).toBe('claims@example.com');
+     expect(client.auth.name).toBe('Claims Test User');

473-516: Consider more specific status code assertions.

Using toBeGreaterThanOrEqual(400) is overly permissive—it would accept 500 (Internal Server Error) when you expect 400/401 (client auth errors). This could mask server bugs.

For auth-related failures (empty Bearer, wrong scheme), the expected response should be 401:

-      // Server should reject with 4xx or 5xx error for invalid format
-      expect(response.status).toBeGreaterThanOrEqual(400);
+      // Server should reject with 401 for invalid/missing token
+      expect(response.status).toBe(401);

If the server implementation genuinely varies, use expect([400, 401]).toContain(response.status) as done in line 361.


582-622: Test doesn't fully verify session isolation.

The test is named "should isolate sessions between different users" but only verifies both users can create tasks independently. It doesn't confirm that user1's tasks are invisible to user2 (and vice versa).

Consider adding isolation verification:

      expect(result2).toBeSuccessful();

+     // Verify isolation: each user should only see their own tasks
+     const user1Tasks = await client1.tools.call('list-tasks', {});
+     const user2Tasks = await client2.tools.call('list-tasks', {});
+     
+     expect(user1Tasks.text()).toContain('User 1 Task');
+     expect(user1Tasks.text()).not.toContain('User 2 Task');
+     expect(user2Tasks.text()).toContain('User 2 Task');
+     expect(user2Tasks.text()).not.toContain('User 1 Task');

      await client1.disconnect();
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts (1)

242-252: Consider adding a negative test case for authUserSchema.

The credentialTypeSchema test at lines 254-264 includes both valid and invalid input verification, but authUserSchema only tests the valid case. Adding a negative case would improve test consistency and coverage.

Suggested enhancement
     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 invalid input (missing required field)
+      const invalidUser = { email: 'user@example.com' };
+      const invalidResult = authUserSchema.safeParse(invalidUser);
+      expect(invalidResult.success).toBe(false);
     });
apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (5)

201-232: Verify intermediate operations succeed.

Lines 211-225 add multiple credentials but don't verify each add-credential call succeeded before checking the aggregate count. If one silently fails, the final assertion '"count":2' will fail without indicating which operation caused the issue.

Proposed fix
       // Add multiple credentials
-      await mcp.tools.call('add-credential', {
+      const addResult1 = await mcp.tools.call('add-credential', {
         entryId,
         appId: 'app1',
         providerId: 'provider1',
         credentialType: 'api_key',
         apiKey: 'key1',
       });
+      expect(addResult1).toBeSuccessful();
 
-      await mcp.tools.call('add-credential', {
+      const addResult2 = await mcp.tools.call('add-credential', {
         entryId,
         appId: 'app2',
         providerId: 'provider2',
         credentialType: 'bearer',
         bearerToken: 'token2',
       });
+      expect(addResult2).toBeSuccessful();

257-291: Verify intermediate operations in consent filtering test.

Lines 267-281 perform update-consent and add-credential without verifying success. This makes debugging harder if the test fails at line 290.

Proposed fix
       // Set consent for specific app
-      await mcp.tools.call('update-consent', {
+      const consentResult = await mcp.tools.call('update-consent', {
         entryId,
         enabled: true,
         selectedToolIds: ['consented-app:tool1'],
         availableToolIds: ['consented-app:tool1', 'other-app:tool1'],
       });
+      expect(consentResult).toBeSuccessful();
 
       // Add credential for consented app
-      await mcp.tools.call('add-credential', {
+      const credResult = await mcp.tools.call('add-credential', {
         entryId,
         appId: 'consented-app',
         providerId: 'provider',
         credentialType: 'api_key',
         apiKey: 'allowed-key',
       });
+      expect(credResult).toBeSuccessful();

466-478: Clarify expected behavior for operations on non-existent entries.

The comment "may fail or succeed depending on implementation" indicates uncertainty about the expected behavior. E2E tests should document definitive contracts. Either:

  1. The API should return a specific error response (update assertions accordingly), or
  2. The API intentionally succeeds silently (document this as intentional idempotent behavior)

Consider adding assertions that verify the specific expected behavior rather than only checking for no crash.

       // This may fail or succeed depending on implementation
-      // The important thing is it doesn't crash
-      expect(result).toBeSuccessful();
+      // API handles non-existent entries gracefully (idempotent design)
+      expect(result).toBeSuccessful();
+      expect(result).toHaveTextContent('not found'); // or whatever the expected message is

522-558: Consider adding expiration validation test.

The test stores both expired and non-expired credentials and verifies the count is 2. Consider adding assertions or a separate test that verifies the system correctly identifies/filters expired credentials when retrieving them for actual use, rather than just confirming storage.


605-641: Add success assertions for intermediate operations in persistence test.

Lines 616-633 perform multiple operations (add-credential, update-consent, authorize-app) without verifying each succeeds. If any intermediate operation fails, the final assertions will fail without clear indication of the root cause.

Proposed fix
       // Add credential
-      await mcp.tools.call('add-credential', {
+      const credResult = await mcp.tools.call('add-credential', {
         entryId,
         appId: 'persist-app',
         providerId: 'persist-provider',
         credentialType: 'bearer',
         bearerToken: 'persist-token',
       });
+      expect(credResult).toBeSuccessful();
 
       // Update consent
-      await mcp.tools.call('update-consent', {
+      const consentResult = await mcp.tools.call('update-consent', {
         entryId,
         enabled: true,
         selectedToolIds: ['persist-app:tool'],
         availableToolIds: ['persist-app:tool'],
       });
+      expect(consentResult).toBeSuccessful();
 
       // Authorize app
-      await mcp.tools.call('authorize-app', { entryId, appId: 'persist-app' });
+      const authResult = await mcp.tools.call('authorize-app', { entryId, appId: 'persist-app' });
+      expect(authResult).toBeSuccessful();
apps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.ts (1)

27-39: Missing error handling for vault operations.

The vault.authorizeApp and vault.isAppAuthorized calls can throw errors, but there's no try/catch block. This is inconsistent with create-pending-auth.tool.ts which properly handles errors and returns { success: false, message: ... }.

Suggested fix: Add error handling
 async execute(input: z.infer<typeof inputSchema>): Promise<z.infer<typeof outputSchema>> {
   const sessionId = this.getAuthInfo().sessionId ?? 'mock-session-default';
   const vault = await getVault(sessionId);

+  try {
     await vault.authorizeApp(input.entryId, input.appId);
     const isAuthorized = await vault.isAppAuthorized(input.entryId, input.appId);

     return {
       success: true,
       isAuthorized,
       message: `App ${input.appId} is ${isAuthorized ? '' : 'not '}authorized`,
     };
+  } catch (error) {
+    return {
+      success: false,
+      isAuthorized: false,
+      message: `Failed to authorize app: ${error instanceof Error ? error.message : String(error)}`,
+    };
+  }
 }
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (2)

31-39: Consider validating that at least one field is provided for update.

The current implementation allows calling vault.update with an empty updates object if neither userEmail nor userName is provided. This is a no-op that may confuse callers.

Optional: Add validation for non-empty updates
     const updates: Record<string, unknown> = {};
     if (input.userEmail !== undefined) {
       updates.userEmail = input.userEmail;
     }
     if (input.userName !== undefined) {
       updates.userName = input.userName;
     }

+    if (Object.keys(updates).length === 0) {
+      return {
+        success: false,
+        message: 'No fields provided to update',
+      };
+    }
+
     await vault.update(input.entryId, updates);

26-45: Missing error handling for vault.update operation.

Similar to other vault tools, vault.update can throw if the entry doesn't exist or another error occurs. Adding try/catch would provide better error feedback to callers.

apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)

30-49: Missing error handling for vault.create operation.

The vault.create call can throw errors (e.g., duplicate entry, storage failure), but unlike CreatePendingAuthTool, this tool doesn't wrap the operation in a try/catch block.

Suggested fix: Add error handling
 async execute(input: z.infer<typeof inputSchema>): Promise<z.infer<typeof outputSchema>> {
   const sessionId = this.getAuthInfo().sessionId ?? 'mock-session-default';
   const vault = await getVault(sessionId);

+  try {
     const entry = await vault.create({
       userSub: input.userSub,
       userEmail: input.userEmail,
       userName: input.userName,
       clientId: input.clientId,
     });

     return {
       success: true,
       entryId: entry.id,
       userSub: entry.userSub,
       clientId: entry.clientId,
       message: `Created vault entry ${entry.id} for user ${input.userSub}`,
     };
+  } catch (error) {
+    return {
+      success: false,
+      entryId: '',
+      userSub: input.userSub,
+      clientId: input.clientId,
+      message: `Failed to create vault entry: ${error instanceof Error ? error.message : String(error)}`,
+    };
+  }
 }
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)

24-35: Missing error handling for vault.delete operation.

The vault.delete operation may throw if the entry doesn't exist or a storage error occurs. Consider adding try/catch for consistency with CreatePendingAuthTool.

apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)

58-64: Consider disconnecting storage adapters in clearAllVaults.

The clearAllVaults function clears the Maps but doesn't call disconnect() on the MemoryStorageAdapter instances. While this may be fine for in-memory adapters in E2E tests, it could cause resource leaks if the adapter implementation holds connections or timers.

♻️ Suggested improvement
 export function clearAllVaults(): void {
+  // Disconnect all storage adapters before clearing
+  for (const storage of storageInstances.values()) {
+    storage.disconnect?.();
+  }
   vaultInstances.clear();
   storageInstances.clear();
+  initPromises.clear();
 }
apps/e2e/demo-e2e-redis/e2e/transport-session.e2e.test.ts (1)

13-17: Consider adding test isolation with beforeEach cleanup.

The tests share the same MCP session, so state set in one test may affect subsequent tests. For example, test('should list all state keys') at Line 73 may pick up keys from earlier tests.

For more deterministic tests, consider adding a cleanup hook or using unique key prefixes per test:

test.describe('Transport Session E2E', () => {
  test.use({
    server: 'apps/e2e/demo-e2e-redis/src/main.ts',
    publicMode: true,
  });

  // If the testing framework supports it:
  // test.beforeEach(async ({ mcp }) => {
  //   await mcp.tools.call('clear-session-state', {});
  // });
apps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.ts (2)

49-85: Consider validating required fields per credential type.

The input schema makes all credential-specific fields optional, but certain fields should be required for each type (e.g., accessToken for oauth, apiKey for api_key). While mock fallbacks work for E2E testing, explicit validation would catch misconfigured test inputs earlier.

♻️ Optional: Use discriminated union for stricter validation
const inputSchema = z.discriminatedUnion('credentialType', [
  z.object({
    credentialType: z.literal('oauth'),
    entryId: z.string(),
    appId: z.string(),
    providerId: z.string(),
    accessToken: z.string(),
    refreshToken: z.string().optional(),
    scopes: z.array(z.string()).optional(),
    expiresAt: z.number().optional(),
  }),
  z.object({
    credentialType: z.literal('api_key'),
    entryId: z.string(),
    appId: z.string(),
    providerId: z.string(),
    apiKey: z.string(),
    headerName: z.string().optional(),
  }),
  // ... other types
]);

80-85: Unreachable default case (defensive but acceptable).

The z.enum(['oauth', 'api_key', 'bearer', 'basic']) ensures credentialType is one of the four values, making this default case unreachable at runtime. This is acceptable as defensive coding, but you could add a TypeScript exhaustiveness check.

♻️ Optional: Add exhaustiveness check
       default:
+        const _exhaustive: never = input.credentialType;
         return {
           success: false,
-          message: `Unsupported credential type: ${input.credentialType}`,
+          message: `Unsupported credential type: ${_exhaustive}`,
         };
apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts (1)

11-20: Output schema sessionId is declared nullable but never returns null.

The sessionId field is typed as z.string().nullable(), but line 30 ensures it's always a non-null string via the ?? 'mock-session-default' fallback. Consider updating the schema to z.string() for accuracy.

♻️ Proposed fix
 const outputSchema = z
   .object({
     success: z.boolean(),
-    sessionId: z.string().nullable(),
+    sessionId: z.string(),
     stateFound: z.boolean(),
     stateValue: z.string().nullable(),
     allStateKeys: z.array(z.string()),
     message: z.string(),
   })
   .strict();
apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts (1)

31-43: Missing error handling for vault operations.

Unlike CreatePendingAuthTool (which wraps vault operations in try-catch and returns a structured error response), this tool lets exceptions propagate. For consistency and better E2E test diagnostics, consider adding error handling.

♻️ Proposed fix
 async execute(input: z.infer<typeof inputSchema>): Promise<z.infer<typeof outputSchema>> {
   const sessionId = this.getAuthInfo().sessionId ?? 'mock-session-default';
   const vault = await getVault(sessionId);

+  try {
     if (input.action === 'complete') {
       await vault.completePendingAuth(input.entryId, input.pendingAuthId);
       return {
         success: true,
         message: `Completed pending auth ${input.pendingAuthId}`,
       };
     } else {
       await vault.cancelPendingAuth(input.entryId, input.pendingAuthId);
       return {
         success: true,
         message: `Cancelled pending auth ${input.pendingAuthId}`,
       };
     }
+  } catch (error) {
+    return {
+      success: false,
+      message: `Failed to ${input.action} pending auth: ${error instanceof Error ? error.message : String(error)}`,
+    };
+  }
 }
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts (1)

44-54: providerId is silently ignored when appId is not provided.

The schema description states "Filter by provider ID (requires appId)", but the code doesn't enforce this. If a caller provides only providerId, it falls through to getAllCredentials, ignoring the filter.

♻️ Option 1: Enforce via schema refinement
 const inputSchema = z
   .object({
     entryId: z.string().describe('Vault entry ID'),
     appId: z.string().optional().describe('Filter by application ID'),
     providerId: z.string().optional().describe('Filter by provider ID (requires appId)'),
   })
-  .strict();
+  .strict()
+  .refine(
+    (data) => !data.providerId || data.appId,
+    { message: 'providerId requires appId to be specified' }
+  );
♻️ Option 2: Return error in execute
   async execute(input: z.infer<typeof inputSchema>): Promise<z.infer<typeof outputSchema>> {
     const sessionId = this.getAuthInfo().sessionId ?? 'mock-session-default';
     const vault = await getVault(sessionId);

+    if (input.providerId && !input.appId) {
+      return {
+        success: false,
+        credentials: [],
+        count: 0,
+        message: 'providerId requires appId to be specified',
+      };
+    }
+
     let credentials;

Note: Option 2 requires adding success: false handling in the output schema or changing it to allow failures.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11b66b9 and d651b3d.

📒 Files selected for processing (52)
  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
  • apps/e2e/demo-e2e-providers/jest.e2e.config.ts
  • apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
  • apps/e2e/demo-e2e-redis/e2e/transport-session.e2e.test.ts
  • apps/e2e/demo-e2e-redis/jest.e2e.config.ts
  • apps/e2e/demo-e2e-redis/src/apps/sessions/resources/session-current.resource.ts
  • apps/e2e/demo-e2e-redis/src/apps/sessions/tools/get-session-data.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/index.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/update-session-state.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/index.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-consent.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/main.ts
  • apps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-transparent/jest.e2e.config.ts
  • apps/e2e/demo-e2e-transparent/src/apps/tasks/tools/create-task.tool.ts
  • apps/e2e/demo-e2e-transparent/src/apps/tasks/tools/list-tasks.tool.ts
  • libs/sdk/src/common/interfaces/execution-context.interface.ts
  • libs/sdk/src/context/__tests__/frontmcp-context-storage.test.ts
  • libs/sdk/src/context/__tests__/frontmcp-context.test.ts
  • libs/sdk/src/context/__tests__/metadata.utils.test.ts
  • libs/sdk/src/context/__tests__/session-key.test.ts
  • libs/sdk/src/context/__tests__/trace-context.test.ts
  • libs/sdk/src/context/frontmcp-context-storage.ts
  • libs/sdk/src/context/frontmcp-context.provider.ts
  • libs/sdk/src/context/frontmcp-context.ts
  • libs/sdk/src/context/index.ts
  • libs/sdk/src/context/metadata.utils.ts
  • libs/sdk/src/context/request-context-storage.ts
  • libs/sdk/src/context/request-context.provider.ts
  • libs/sdk/src/context/request-context.ts
  • libs/sdk/src/context/session-key.provider.ts
  • libs/sdk/src/provider/__tests__/provider-session-scope.test.ts
  • libs/sdk/src/provider/provider.registry.ts
  • libs/sdk/src/scope/scope.instance.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/e2e/demo-e2e-transparent/jest.e2e.config.ts
  • apps/e2e/demo-e2e-redis/jest.e2e.config.ts
  • apps/e2e/demo-e2e-providers/jest.e2e.config.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • apps/e2e/demo-e2e-redis/e2e/transport-session.e2e.test.ts
  • apps/e2e/demo-e2e-transparent/src/apps/tasks/tools/create-task.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/index.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/index.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/sessions/tools/get-session-data.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.ts
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/update-session-state.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.ts
  • apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts
  • apps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-consent.tool.ts
  • apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/sessions/resources/session-current.resource.ts
  • apps/e2e/demo-e2e-transparent/src/apps/tasks/tools/list-tasks.tool.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-redis/src/main.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Include instanceof checks in tests for error classes to verify proper error hierarchy

Files:

  • apps/e2e/demo-e2e-redis/e2e/transport-session.e2e.test.ts
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
  • apps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
🧠 Learnings (24)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)

Applied to files:

  • apps/e2e/demo-e2e-redis/e2e/transport-session.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/index.ts
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/universal/**/*.{ts,tsx} : Universal app shell (UniversalApp, FrontMCPProvider) must provide platform-agnostic React context and initialization

Applied to files:

  • apps/e2e/demo-e2e-redis/src/apps/transport/index.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/index.ts
  • apps/e2e/demo-e2e-redis/src/main.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-redis/src/main.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/main.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-redis/src/main.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,utils}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test HTML escaping for user-provided content to prevent XSS vulnerabilities

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/*.{test,spec}.{ts,tsx,js,jsx} : Every component and utility must test invalid inputs and edge cases

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : SSR components must be tested for correct HTML output and client-side hydration must be tested separately

Applied to files:

  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext

Applied to files:

  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Extend tool metadata using `declare global` pattern to allow tools to specify plugin-specific options in their decorators

Applied to files:

  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rate

Applied to files:

  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Do not use prefixes like 'PT-001' in test names

Applied to files:

  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/package.json : Entry points must match the documented paths: frontmcp/ui/react, frontmcp/ui/renderers, frontmcp/ui/render, frontmcp/ui/universal, frontmcp/ui/bundler, frontmcp/ui/bridge, frontmcp/ui/components, frontmcp/ui/layouts, frontmcp/ui/web-components

Applied to files:

  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Extend ExecutionContextBase with plugin-specific properties using module declaration (`declare module 'frontmcp/sdk'`) combined with `contextExtensions` in plugin metadata

Applied to files:

  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Use module augmentation for context properties via `declare module 'frontmcp/sdk'` combined with runtime plugin metadata `contextExtensions`, not module-level side effects

Applied to files:

  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src,tests}/**/*.{ts,tsx,js,jsx} : Maintain 95%+ code coverage across statements, branches, functions, and lines

Applied to files:

  • apps/e2e/demo-e2e-config/jest.e2e.config.ts
  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Configure approval requirements on tools using the `approval` decorator property with fields: `required`, `defaultScope`, `category`, `riskLevel`, and `approvalMessage`

Applied to files:

  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.test.{ts,tsx} : Test coverage must be 95%+ across statements, branches, functions, and lines

Applied to files:

  • apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
🧬 Code graph analysis (17)
apps/e2e/demo-e2e-transparent/src/apps/tasks/tools/create-task.tool.ts (2)
libs/sdk/src/common/interfaces/execution-context.interface.ts (1)
  • authInfo (89-91)
libs/sdk/src/context/frontmcp-context.ts (1)
  • authInfo (244-246)
apps/e2e/demo-e2e-redis/src/apps/transport/index.ts (1)
apps/e2e/demo-e2e-redis/src/apps/vault/index.ts (1)
  • App (13-29)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
  • Tool (24-50)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)
  • Tool (20-46)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-49)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/sessions/tools/get-session-data.tool.ts (1)
  • Tool (17-37)
apps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.ts (1)
  • Tool (21-41)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/update-session-state.tool.ts (1)
  • Tool (21-43)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.ts (1)
  • Tool (36-103)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
  • Tool (34-74)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-49)
apps/e2e/demo-e2e-redis/src/apps/vault/index.ts (1)
apps/e2e/demo-e2e-redis/src/apps/transport/index.ts (1)
  • App (7-12)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (5)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.ts (1)
  • Tool (36-103)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
  • Tool (24-50)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
  • Tool (18-36)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
  • Tool (34-74)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-consent.tool.ts (1)
  • Tool (22-48)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts (2)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.ts (1)
  • Tool (25-58)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-49)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-49)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.ts (4)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
  • Tool (18-36)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
  • Tool (34-74)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)
  • Tool (20-46)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-49)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (1)
libs/sdk/src/scope/scope.instance.ts (1)
  • tools (333-335)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/update-session-state.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.ts (1)
  • Tool (21-41)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts (1)
  • Tool (22-55)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.ts (1)
  • Tool (20-44)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (4)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
  • Tool (24-50)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
  • Tool (34-74)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)
  • Tool (20-46)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-49)
apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts (2)
libs/sdk/src/scope/scope.instance.ts (4)
  • auth (313-315)
  • tools (333-335)
  • resources (341-343)
  • prompts (345-347)
libs/sdk/src/context/frontmcp-context.ts (1)
  • fetch (467-502)
apps/e2e/demo-e2e-transparent/src/apps/tasks/tools/list-tasks.tool.ts (2)
libs/sdk/src/common/interfaces/execution-context.interface.ts (1)
  • authInfo (89-91)
libs/sdk/src/context/frontmcp-context.ts (1)
  • authInfo (244-246)
apps/e2e/demo-e2e-redis/src/main.ts (12)
apps/e2e/demo-e2e-public/src/main.ts (1)
  • FrontMcp (6-15)
apps/e2e/demo-e2e-agents/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-cache/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-codecall/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-errors/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-notifications/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-openapi/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-providers/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-remember/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-remote/src/main.ts (1)
  • FrontMcp (8-55)
apps/e2e/demo-e2e-serverless/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-ui/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.ts (2)
apps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.ts (1)
  • Tool (21-41)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts (1)
  • Tool (22-55)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts (1)

580-597: Add test coverage for rate limiting and signature verification features.

Verification confirms that the redis-session.store.ts implementation includes two critical features that lack test coverage in redis-session.store.test.ts:

  1. Rate Limiting: Initialized in the constructor (lines 61-64) using SessionRateLimiter when security.enableRateLimiting is true, but no tests cover this functionality.

  2. Signature Verification: Used in both the get() method (lines 173-177) and set() method (lines 265-266) via verifyOrParseSession() and signSession() when security.enableSigning is true, but no tests exercise these paths.

These are core security features that must be tested to meet the 95%+ code coverage requirement and ensure proper behavior when signing and rate limiting are enabled.

🧹 Nitpick comments (2)
libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts (2)

116-123: Consider using a properly typed mock instead of {} as never.

Using as never is a type escape hatch that circumvents TypeScript's type checking. While it avoids any, it still bypasses type safety. Consider creating a minimal typed mock object or using a library like jest-mock-extended for proper Redis mock typing.

♻️ Suggested improvement
-      const mockRedis = {} as never;
+      // Create a minimal mock satisfying the Redis interface requirements
+      const mockRedis = {
+        status: 'ready',
+        disconnect: jest.fn(),
+      } as unknown as Redis;

This provides slightly better documentation of the mock's intended shape while still allowing the test to compile.


203-212: Avoid non-null assertion - use proper type guard instead.

Line 211 uses result!.lastAccessedAt which violates the coding guidelines that state to avoid non-null assertions (!) and use proper error handling and type guards instead.

♻️ Proposed fix
       const result = await store.get('test-session-id');
 
       expect(result).not.toBeNull();
-      expect(result!.lastAccessedAt).toBeGreaterThanOrEqual(originalLastAccessed);
+      expect(result?.lastAccessedAt).toBeGreaterThanOrEqual(originalLastAccessed);

Using optional chaining (?.) is safer here. Since the assertion expect(result).not.toBeNull() runs first, the test will fail appropriately if result is null, and the optional chaining prevents potential runtime issues while satisfying TypeScript.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d651b3d and 2b0e14f.

📒 Files selected for processing (16)
  • libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/redis-session.store.ts
  • libs/sdk/src/auth/session/session-store.factory.ts
  • libs/sdk/src/auth/session/vercel-kv-session.store.ts
  • libs/sdk/src/store/adapters/store.base.adapter.ts
  • libs/sdk/src/store/adapters/store.memory.adapter.ts
  • libs/sdk/src/store/adapters/store.redis.adapter.ts
  • libs/sdk/src/store/adapters/store.vercel-kv.adapter.ts
  • libs/sdk/src/store/index.ts
  • libs/sdk/src/store/store.helpers.ts
  • libs/sdk/src/store/store.registry.ts
  • libs/sdk/src/store/store.tokens.ts
  • libs/sdk/src/store/store.types.ts
  • libs/sdk/src/store/store.utils.ts
  • libs/sdk/src/transport/transport.registry.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/session/index.ts
  • libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Include instanceof checks in tests for error classes to verify proper error hierarchy

Files:

  • libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files

Applied to files:

  • libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases

Applied to files:

  • libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use `getCapabilities()` for dynamic capability exposure instead of hardcoding capabilities

Applied to files:

  • libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.test.ts : Tests must achieve 95%+ code coverage across all metrics; use `MockStore` implementing `RememberStoreInterface` for RememberPlugin testing

Applied to files:

  • libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
🧬 Code graph analysis (1)
libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts (1)
libs/sdk/src/auth/session/redis-session.store.ts (1)
  • RedisSessionStore (35-345)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Libraries
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts (6)

10-31: LGTM!

The mock setup is well-structured. Using jest.requireActual to preserve other exports while mocking only RedisStorageAdapter is the correct approach for partial mocking. The separation of mockStorageAdapter for adapter methods and mockRedisClient for direct Redis operations (GETEX) aligns with the implementation architecture.


241-292: LGTM!

The set() tests properly verify:

  • TTL conversion from milliseconds (60000) to seconds (60)
  • Fallback to session's expiresAt when no TTL is explicitly provided
  • Storage without TTL when neither explicit TTL nor expiresAt is present
  • Proper JSON serialization

The assertions correctly reflect the adapter-based architecture where TTL is specified in seconds via { ttlSeconds } options.


305-315: LGTM!

The test correctly verifies that the session ID is passed to the adapter without manual prefix application. The comment at lines 313-314 appropriately clarifies that key prefix handling is delegated to the adapter, which aligns with the architectural shift from direct Redis calls to adapter-based storage.


418-430: LGTM!

The getRedisClient() tests cover both the success case (returning the client from the adapter) and the edge case (returning undefined when the adapter has no client). This provides good coverage for the delegation pattern.


446-453: Good error handling coverage.

The tests for empty sessionId rejection properly verify that all CRUD operations throw with the expected error message '[RedisSessionStore] sessionId cannot be empty'. This ensures consistent validation across the API surface.


401-411: No action required—mock isolation is properly handled.

The beforeEach hook calls jest.clearAllMocks() before each test runs, ensuring the external connection test starts with a clean mock state. When externalStore.disconnect() is called on line 407, the implementation correctly checks if (!this.externalInstance) (line 319 of the implementation) and skips the adapter call since the instance was created with an external Redis client. The assertion at line 410 is correct and the test isolation works as intended.

libs/sdk/src/auth/session/index.ts (2)

27-47: Good consolidation - re-exporting authorization APIs from @frontmcp/auth.

This properly maintains backward compatibility for SDK consumers by re-exporting the authorization store functionality from the new @frontmcp/auth package. The clean separation between value exports and type exports using export type is correct.

The organized grouping (Classes, Functions, Schemas, Types) with comments aids readability. All exported symbols are correctly defined in @frontmcp/auth.


7-8: Replace generic Error with MCP-specific error class at line 114.

The createSessionStoreSync function throws a generic Error when Vercel KV provider is used, but should use a specific MCP error class per the libs/sdk guidelines. Replace with PublicMcpError (or appropriate public/internal error type) with an MCP error code:

throw new PublicMcpError('Vercel KV session store requires async initialization. Use createSessionStore() instead.', 'INVALID_SESSION_STORE_CONFIGURATION', 400);

The factory functions themselves are well-implemented with proper type safety and lazy loading optimization. The exports in index.ts lines 7-8 are clean and correctly structured.

⛔ Skipped due to learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errors
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx,js,jsx} : Do not expose internal error details in public APIs - use sanitized error messages
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts:
- Around line 121-129: The test named 'should handle missing optional providers
gracefully' is misleading because ConfigApp always registers AppConfigProvider
and RequestLoggerProvider under publicMode: true and the test only calls
mcp.tools.call('get-app-info'); either rename the test to reflect it is a basic
smoke test (e.g., 'should work with all providers available') or add a new test
that constructs ConfigApp without AppConfigProvider/RequestLoggerProvider (or
toggles their registration) and then calls mcp.tools.call('get-app-info') to
assert graceful degradation; update the test title or add the new test
accordingly and keep the original smoke-check if desired.

In @apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts:
- Around line 261-296: The test omits validating the result of the
update-consent RPC call; capture the response from
mcp.tools.call('update-consent') and assert it succeeded before continuing
(e.g., assert success and expected payload fields like
selectedToolIds/availableToolIds or an updated consent flag) so that adding
credentials and later assertions only run when consent was actually updated;
update the test "should filter credentials based on consent" to check the
update-consent call result (the call named 'update-consent' and its response)
and fail early if it indicates an error or unexpected state.
- Around line 112-146: Add an assertion that the vault entry creation succeeded
before extracting entryId: after each call that assigns createResult (the
mcp.tools.call('create-vault-entry') calls used in the tests that extract
entryId), assert createResult.toBeSuccessful() (or the equivalent test assertion
helper used elsewhere) immediately before calling createResult.json() and
reading entryId; apply this change in the current test (where createResult is
extracted) and the other credential tests that extract entryId (the same pattern
at the other createResult occurrences referenced in the review).
- Around line 610-646: The test "should persist vault entries across multiple
tool calls" currently performs several mcp.tools.call operations without
verifying each call succeeded; add immediate assertions after createResult and
after the calls to 'add-credential', 'update-consent', and 'authorize-app' to
validate their responses (e.g., check status/success flags, returned IDs, or
expected JSON fields) so failures surface at the exact operation (use the
existing createResult, the add/credential/update-consent/authorize-app call
results, and get-vault-entry to assert expected values).

In @apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts:
- Around line 61-71: The clearAllVaults function fails to clear in-flight
initialization trackers (initPromises) so resolved inits can repopulate
vaultInstances/storageInstances after clearing; fix by snapshotting current
initPromises (Array.from(initPromises.values())), immediately clearing
initPromises (initPromises.clear()) to mark them cancelled, then await
Promise.all on the snapshot to let any in-flight work finish before
disconnecting storage and finally clear vaultInstances and storageInstances;
update clearAllVaults to reference initPromises, vaultInstances,
storageInstances and the existing disconnect logic accordingly.
🧹 Nitpick comments (8)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)

31-40: Consider validating that at least one update field is provided.

If both userEmail and userName are undefined, the updates object will be empty, resulting in a potentially unnecessary or confusing vault update call. Consider returning an early error or success message indicating no changes were requested.

Suggested validation
     try {
       const updates: Record<string, unknown> = {};
       if (input.userEmail !== undefined) {
         updates.userEmail = input.userEmail;
       }
       if (input.userName !== undefined) {
         updates.userName = input.userName;
       }

+      if (Object.keys(updates).length === 0) {
+        return {
+          success: false,
+          message: 'No fields to update. Provide at least userEmail or userName.',
+        };
+      }
+
       await vault.update(input.entryId, updates);
apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (3)

33-52: Consider adding error handling for json() parsing.

At line 43, if createResult doesn't contain valid JSON or the expected structure, json<{ entryId: string }>() could throw or return undefined. While line 40 asserts success, consider defensive handling or type guards for robustness.

💡 Suggested improvement
       expect(createResult).toBeSuccessful();

       // Extract entry ID from response using json() method
       const parsed = createResult.json<{ entryId: string }>();
+      expect(parsed.entryId).toBeDefined();
       const entryId = parsed.entryId;

442-525: Consider making error handling assertions more deterministic.

Several tests in this section have comments like "depends on implementation" (line 449, 480) or "may fail or succeed" (line 523). While testing that operations don't crash is valuable, these tests could be more useful if they verified the specific expected behavior. If the behavior is undefined, consider documenting the expected contract first.

Example improvements:

  • Line 449-450: If update on non-existent entry should be a no-op, assert the response indicates this
  • Line 480-482: If adding credential to non-existent vault should fail, assert the failure message
  • Line 523: Define whether completing non-existent pending auth should return an error

527-563: Good edge case coverage for credential expiration states.

Testing credentials with past expiration (line 543) and far-future expiration (line 555) is valuable. However, consider adding a test that verifies the system's behavior when retrieving expired credentials - does it filter them out or return them with an expiration flag?

apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)

31-45: Init failure leaves stale rejected promise in cache.

If storage.connect() or vault construction throws, initPromises.delete(sessionId) at line 43 won't execute. Subsequent calls to getVault(sessionId) will return the cached rejected promise instead of retrying initialization.

Proposed fix with try/finally
   const initPromise = (async () => {
+    try {
       const storage = new MemoryStorageAdapter();
       await storage.connect(); // Connect the adapter
       storageInstances.set(sessionId, storage);

       const vault = new StorageAuthorizationVault(storage, {
         namespace: 'vault',
         pendingAuthTtlMs: 60000, // 1 minute for testing
         validateOnRead: false, // Disable for simpler testing
       });

       vaultInstances.set(sessionId, vault);
-      initPromises.delete(sessionId);
       return vault;
+    } finally {
+      initPromises.delete(sessionId);
+    }
   })();
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (3)

52-69: Credential scope pattern tests have nearly identical assertions.

Both credential scope pattern - machine/global and credential scope pattern - session tests assert only that the response contains 'instanceId'. The tests don't actually verify the distinguishing behavior between machine/global and session scopes.

Consider adding more specific assertions:

  • For machine/global: Verify consistent instanceId across multiple calls (like the GLOBAL scope test does)
  • For session: Verify providerScope is CONTEXT or that instances differ across sessions
♻️ Suggested enhancement
   test('should demonstrate credential scope pattern - machine/global', async ({ mcp }) => {
     // Machine/global scope credentials would be shared across all users
     // This tests the pattern without actual credential storage
-    const result = await mcp.tools.call('get-app-info', {});
+    const result1 = await mcp.tools.call('get-app-info', {});
+    const result2 = await mcp.tools.call('get-app-info', {});

-    expect(result).toBeSuccessful();
+    expect(result1).toBeSuccessful();
+    expect(result2).toBeSuccessful();
     // Instance ID being consistent across calls demonstrates global/machine scope
-    expect(result).toHaveTextContent('instanceId');
+    expect(result1).toHaveTextContent('"providerScope":"GLOBAL"');
+    expect(result2).toHaveTextContent('"providerScope":"GLOBAL"');
   });

   test('should demonstrate credential scope pattern - session', async ({ mcp }) => {
     // Session scope credentials are tied to the current session
     const result = await mcp.tools.call('get-request-info', {});

     expect(result).toBeSuccessful();
     // Request ID being session-scoped demonstrates session credential pattern
-    expect(result).toHaveTextContent('instanceId');
+    expect(result).toHaveTextContent('"providerScope":"CONTEXT"');
   });

91-97: Consider guarding against undefined messages property.

Line 96 accesses result.messages.length directly after expect(result).toBeSuccessful(). If the result structure is unexpected or messages is undefined, this will throw a runtime error before the assertion can fail gracefully.

♻️ Optional defensive check
   test('should access provider in prompt context', async ({ mcp }) => {
     // Prompts can access providers via DI
     const result = await mcp.prompts.get('debug-context', {});

     expect(result).toBeSuccessful();
+    expect(result.messages).toBeDefined();
     expect(result.messages.length).toBeGreaterThan(0);
   });

149-156: Type guard may cause silent test pass if content type is unexpected.

The type guard if (message.content.type === 'text') correctly narrows the type, but if the content type is not 'text', the test will pass without verifying the expected content. Consider adding an explicit expectation or failure path.

♻️ Ensure assertions are always executed
       const message = result.messages[0];
-      expect(message.content.type).toBe('text');
-      if (message.content.type === 'text') {
-        // Prompt accesses both GLOBAL and CONTEXT providers
-        expect(message.content.text).toContain('GLOBAL Scope Provider');
-        expect(message.content.text).toContain('CONTEXT Scope Provider');
-      }
+      expect(message.content.type).toBe('text');
+      // Type assertion is safe here since we asserted the type above
+      const textContent = (message.content as { type: 'text'; text: string }).text;
+      // Prompt accesses both GLOBAL and CONTEXT providers
+      expect(textContent).toContain('GLOBAL Scope Provider');
+      expect(textContent).toContain('CONTEXT Scope Provider');
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b0e14f and 655e2ae.

📒 Files selected for processing (23)
  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
  • apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts
  • apps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.ts
  • libs/sdk/src/auth/session/redis-session.store.ts
  • libs/sdk/src/auth/session/session-store.factory.ts
  • libs/sdk/src/auth/session/session.schema.ts
  • libs/ui/src/render/react-dom-static.d.ts
  • libs/utils/src/storage/adapters/redis.ts
  • plugins/plugin-approval/src/approval/errors.d.ts
  • plugins/plugin-approval/src/approval/factories.d.ts
  • plugins/plugin-approval/src/approval/guards.d.ts
  • plugins/plugin-approval/src/approval/index.d.ts
  • plugins/plugin-approval/src/approval/schemas.d.ts
  • plugins/plugin-approval/src/approval/types.d.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts
  • apps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts
  • apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Include instanceof checks in tests for error classes to verify proper error hierarchy

Files:

  • apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
  • apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext

Applied to files:

  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)

Applied to files:

  • apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
🧬 Code graph analysis (5)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-49)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
  • Tool (18-43)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.ts (1)
  • Tool (25-58)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-49)
apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts (2)
libs/sdk/src/scope/scope.instance.ts (4)
  • auth (313-315)
  • tools (333-335)
  • resources (341-343)
  • prompts (345-347)
libs/sdk/src/context/frontmcp-context.ts (1)
  • fetch (467-502)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (1)
libs/sdk/src/scope/scope.instance.ts (1)
  • tools (333-335)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
  • Tool (18-43)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
  • Tool (34-74)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-49)
🪛 Gitleaks (8.30.0)
apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts

[high] 196-196: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Build Libraries
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (19)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (2)

26-52: Implementation is consistent with existing vault tools.

The tool follows the established patterns from DeleteVaultEntryTool and GetVaultEntryTool (session fallback, error handling, structured responses). The error handling properly catches exceptions and returns descriptive failure messages.


5-11: Input schema is well-defined with proper Zod v4 syntax.

The schema uses appropriate validators including .email() for the email field and .strict() to reject unknown properties. This aligns with Zod v4 patterns.

apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts (8)

1-19: LGTM!

The file header clearly documents the test purpose and scope. The import from @frontmcp/testing and server configuration via test.use follow the established e2e testing patterns in the codebase.


21-88: LGTM!

The stateful session compatibility tests are well-structured with:

  • Proper client cleanup via disconnect() in all tests
  • Clear assertions for session creation, persistence, and ID stability
  • Helpful inline comment explaining mock data behavior on lines 63-64

90-123: LGTM!

Token signing compatibility tests properly verify:

  • Locally signed tokens are accepted
  • User claims are correctly extracted from signed tokens
  • Proper use of optional chaining for user property access

125-140: LGTM!

JWKS endpoint test properly validates the well-known endpoint structure. The assertions verify the essential JWKS format requirements (keys array with at least one signing key).


142-159: LGTM!

The OAuth authorization server metadata test appropriately handles federation scenarios by accepting redirects. The permissive validation approach is suitable for backward compatibility testing where the goal is to ensure the endpoint exists and responds, not to enforce specific metadata fields.


161-228: LGTM!

Comprehensive MCP capability tests covering tools, resources, and prompts operations. The tests properly:

  • Verify listing operations return results
  • Validate tool call success with custom matchers
  • Clean up client connections consistently

230-268: LGTM!

Error response tests properly validate:

  • 401 status for unauthenticated requests with WWW-Authenticate header verification
  • 401 status for invalid token scenarios

The tests intentionally use raw fetch to bypass the test framework's auth handling, which is the correct approach for testing error responses.


270-352: LGTM!

Multi-user and session data tests are well-designed:

  • Session isolation test verifies different users have separate sessions and identities
  • Concurrent users test efficiently uses Promise.all for both client creation and cleanup
  • Unique session ID verification using Set (line 321-322) is a clean approach
  • Session data persistence test validates multiple operations within a single session
apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (3)

1-17: LGTM! Well-structured test setup.

The test file header clearly documents the scope of tests, and the import/configuration setup is clean. Using publicMode: true for e2e vault testing is appropriate.


181-201: Static analysis false positive - test token is placeholder data.

The Gitleaks warning at line 196 flagging eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... as a "Generic API Key" is a false positive. This is clearly a truncated placeholder JWT used for testing purposes, not a real secret. The token is incomplete and used only within the test context.


386-397: Verify the expected behavior for non-existent vault pending auth.

The test expects toBeSuccessful() on line 393 but then checks for 'Failed' in content on line 394. This suggests the API returns HTTP 200 with an error message in the body. While this may be intentional, it's worth verifying this is the expected behavior, as it could make error handling more difficult for consumers who rely on success/failure status.

apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts (1)

1-52: LGTM!

The implementation correctly follows the established pattern for vault tools. The input/output schemas use proper Zod 4 syntax, error handling returns structured responses, and the action branching logic is clean.

apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)

1-12: Good use of @frontmcp/utils for storage adapter.

The import from @frontmcp/utils follows the coding guidelines for using shared utilities rather than direct implementations.

apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)

1-60: LGTM!

The tool implementation is consistent with other vault tools in the PR. Input schema correctly uses Zod 4's .email() validator for optional email, and error handling follows the established pattern of returning structured responses.

apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (3)

1-19: Well-documented test file header and setup.

The documentation clearly outlines the test scope and appropriately notes that full OAuth/API key credential testing is handled in a separate app. The test configuration using @frontmcp/testing with the correct server path is appropriate for e2e testing.


100-119: Factory pattern tests look good.

The tests appropriately verify that factory-created providers include expected timestamp properties (createdAt, startedAt), demonstrating both synchronous and async initialization patterns.


175-219: Well-structured lifecycle tests with proper singleton verification.

The parallel call pattern using Promise.all effectively tests concurrent access, and the regex-based instance ID extraction correctly verifies singleton behavior for GLOBAL providers and session consistency for CONTEXT providers. The null checks before comparisons are appropriate.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (3)

31-41: Consider consistent assertion patterns.

Lines 35-36 use manual JSON.stringify + string matching, while lines 61-62 use the cleaner .toHaveTextContent() matcher. The manual approach is more fragile if the serialization format changes.

For instanceId extraction (lines 39-41), consider extracting a helper function since this pattern is repeated in lines 193-197 and 215-219.

♻️ Suggested helper extraction
// Helper at top of file or in a shared test utils
function extractInstanceId(result: unknown, pattern: RegExp): string | null {
  const content = JSON.stringify(result);
  const match = content.match(pattern);
  return match ? match[1] : null;
}

125-143: Consider follow-up for graceful degradation coverage.

The smoke tests are appropriate, but the comment correctly notes that full graceful degradation testing (when providers are missing or fail) requires a dedicated test app. Consider tracking this as a follow-up task if not already planned.

Would you like me to open an issue to track adding a dedicated test app for graceful degradation scenarios?


155-162: Type assertion could be improved with a type guard.

The Jest assertion on line 156 doesn't narrow TypeScript's type, so the manual type assertion on line 158 bypasses type safety. Per coding guidelines about avoiding unsafe assertions, consider using a proper type guard or structured access.

♻️ Safer alternative using type guard
       const message = result.messages[0];
-      expect(message.content.type).toBe('text');
-      // Type assertion is safe since we asserted the type above
-      const textContent = (message.content as { type: 'text'; text: string }).text;
+      expect(message.content).toHaveProperty('type', 'text');
+      expect(message.content).toHaveProperty('text');
+      const textContent = 'text' in message.content && typeof message.content.text === 'string' 
+        ? message.content.text 
+        : '';
+      expect(textContent).not.toBe(''); // Ensure we got valid content
       // Prompt accesses both GLOBAL and CONTEXT providers
       expect(textContent).toContain('GLOBAL Scope Provider');
       expect(textContent).toContain('CONTEXT Scope Provider');
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)

32-47: Consider using a more specific type for the updates object.

Using Record<string, unknown> weakens type safety. The vault.update() method accepts Partial<AuthorizationVaultEntry>, so a more specific type would improve type safety while remaining compatible.

🔧 Suggested improvement
-      const updates: Record<string, unknown> = {};
+      const updates: { userEmail?: string; userName?: string } = {};
       if (input.userEmail !== undefined) {
         updates.userEmail = input.userEmail;
       }
       if (input.userName !== undefined) {
         updates.userName = input.userName;
       }
apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (3)

182-184: Prefer structured assertions over string matching for JSON content.

Using toHaveTextContent('"count":1') is brittle and could break if the JSON formatting changes. Consider using the json() method for more robust assertions.

Suggested improvement
-      expect(getResult).toBeSuccessful();
-      expect(getResult).toHaveTextContent('"count":1');
+      expect(getResult).toBeSuccessful();
+      const credResult = getResult.json<{ count: number }>();
+      expect(credResult.count).toBe(1);

This pattern applies to similar assertions at lines 244, 308, 481, 575, and 665.


484-496: Clarify test expectations for error scenarios.

The comment "This may fail or succeed depending on implementation" suggests the test isn't validating specific behavior. If the expected behavior is undefined, this test may pass regardless of whether the implementation is correct.

Consider either:

  1. Defining the expected behavior and asserting accordingly
  2. Checking for a specific error message or status if failure is expected
  3. Documenting why success is acceptable for non-existent entries
       // This may fail or succeed depending on implementation
       // The important thing is it doesn't crash
       expect(result).toBeSuccessful();
+      // Verify specific expected behavior - e.g., error message or empty result
+      expect(result).toHaveTextContent('not found');

Similar concerns apply to tests at lines 498-506 and 508-518.


540-576: Test should verify expiration handling behavior, not just storage.

This test stores both expired and valid credentials and verifies they're both stored ("count":2), but doesn't validate how the system handles expired credentials on retrieval. Should expired credentials be filtered out, flagged, or returned as-is?

Consider adding assertions that verify the actual expiration handling behavior:

       // Get all credentials - both should be stored
       const getResult = await mcp.tools.call('get-credentials', { entryId });
       expect(getResult).toHaveTextContent('"count":2');
+
+      // Verify individual credential expiration states are accessible
+      const creds = getResult.json<{ credentials: Array<{ appId: string; expiresAt?: number }> }>();
+      const expiredCred = creds.credentials.find(c => c.appId === 'expired-app');
+      const futureCred = creds.credentials.find(c => c.appId === 'future-app');
+      
+      // Assert expected expiration handling behavior
+      expect(expiredCred).toBeDefined();
+      expect(futureCred).toBeDefined();
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 655e2ae and 9359319.

📒 Files selected for processing (6)
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
  • apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts
  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts
  • libs/sdk/package.json
  • libs/utils/package.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts
  • libs/sdk/package.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts
  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
  • apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Include instanceof checks in tests for error classes to verify proper error hierarchy

Files:

  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
  • apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)

Applied to files:

  • apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
🧬 Code graph analysis (2)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (4)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
  • Tool (24-60)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
  • Tool (18-43)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
  • Tool (34-74)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
  • getVault (17-52)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (2)
scripts/bump-synchronized-versions.mjs (1)
  • result (130-130)
libs/sdk/src/scope/scope.instance.ts (1)
  • tools (333-335)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Build Libraries
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (4)

1-20: Well-documented test setup.

The header documentation clearly explains the test scope and intent, and the configuration is appropriate for E2E testing of auth providers integration patterns.


75-102: LGTM!

The context extension tests properly validate DI accessibility across tools, resources, and prompts.


104-123: LGTM!

The factory pattern tests appropriately validate instantiation behavior without coupling to implementation details.


181-225: Good lifecycle validation tests.

The concurrent call pattern with Promise.all effectively validates singleton behavior for GLOBAL providers and session persistence for CONTEXT providers.

Note: The regex patterns on lines 195 and 217 have hardcoded prefixes (instance-, req-). This is acceptable for E2E tests but creates coupling to implementation details. If ID formats change, these tests will need updates.

apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (3)

1-3: LGTM!

Imports are appropriate and consistent with other vault tools in the same directory.


5-18: LGTM!

Input and output schemas are well-defined with proper validation. Using .strict() ensures no unexpected properties are passed, and the email validation via z.string().email() is a good practice.


26-59: LGTM!

The execute method follows established patterns from other vault tools:

  • Consistent session ID handling with fallback
  • Proper error handling with informative messages
  • Correct use of !== undefined checks to allow empty strings
  • Early return for validation failure (no fields to update)

The error handling pattern aligns with CreateVaultEntryTool and DeleteVaultEntryTool.

apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (4)

1-17: LGTM! Well-structured test file with clear documentation.

The file header clearly documents the test coverage areas, and the test configuration properly sets up the server and public mode for E2E testing.


19-110: LGTM! Comprehensive CRUD test coverage.

The CRUD tests properly validate create, read, update, and delete operations with appropriate assertions. Good use of the json() method for extracting structured response data.


623-668: Good comprehensive integration test for persistence validation.

This test effectively validates that data persists across multiple tool calls in a realistic workflow. The test properly chains operations and verifies the final state.


13-17: Review comment is based on a valid pattern but overstates the concern.

Tests use unique identifiers to prevent interference, which is sufficient for this in-memory mocked Redis store. While cleanup functions exist (clearAllVaults(), clearAllSessions() in the data layer), they aren't explicitly called in the tests. This is acceptable since:

  • The store is mocked in-memory (simulates Redis for E2E testing), not persistent
  • Data doesn't accumulate across test runs
  • Unique identifiers prevent conflicts within a single run

If you prefer explicit cleanup for hygiene, consider adding:

test.afterEach(async ({ mcp }) => {
  // Call cleanup if needed
});

However, the current approach is sufficient given the test isolation strategy.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
libs/sdk/src/auth/flows/session.verify.flow.ts (1)

114-142: Consider using @frontmcp/utils for UUID generation.

Direct usage of crypto.randomUUID() appears in multiple places (lines 116, 137, 142). Per coding guidelines, cryptographic operations should use @frontmcp/utils instead of direct crypto APIs for consistency and cross-platform support.

♻️ Suggested refactor
+import { randomUUID } from '@frontmcp/utils';
...
-        : { sub: `anon:${crypto.randomUUID()}`, iss: issuer, name: 'Anonymous', scope: scopes.join(' ') };
+        : { sub: `anon:${randomUUID()}`, iss: issuer, name: 'Anonymous', scope: scopes.join(' ') };
...
-      sub: `anon:${crypto.randomUUID()}`,
+      sub: `anon:${randomUUID()}`,
...
-    const uuid = crypto.randomUUID();
+    const uuid = randomUUID();

Based on coding guidelines: "Use @frontmcp/utils for cryptographic operations instead of node:crypto directly."

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9359319 and a941cf6.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (2)
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • package.json
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use strict TypeScript mode with no any types without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use @frontmcp/utils for cryptographic operations instead of node:crypto directly
Use @frontmcp/utils for file system operations instead of fs/promises or node:fs directly

Files:

  • libs/sdk/src/auth/flows/session.verify.flow.ts
libs/{sdk,adapters}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities

Files:

  • libs/sdk/src/auth/flows/session.verify.flow.ts
libs/sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/**/*.{ts,tsx}: Prefer interface for defining object shapes in TypeScript, avoid any types
Use type parameters with constraints instead of unconstrained generics with any defaults
Validate URIs per RFC 3986 at metadata level using isValidMcpUri refinement
Use changeScope instead of scope in change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g., Promise<GetPromptResult>) instead of Promise<unknown> for MCP entry methods
Use unknown instead of any for generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows

Files:

  • libs/sdk/src/auth/flows/session.verify.flow.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/auth/flows/session.verify.flow.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/auth/flows/session.verify.flow.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly

Applied to files:

  • libs/sdk/src/auth/flows/session.verify.flow.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/sdk/src/auth/flows/session.verify.flow.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/sdk/src/auth/flows/session.verify.flow.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Build Libraries
  • GitHub Check: Package E2E (Verdaccio)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
libs/sdk/src/auth/flows/session.verify.flow.ts (5)

23-23: LGTM - Import migration to @frontmcp/auth.

The import source change aligns with the PR objective to centralize auth utilities in the published @frontmcp/auth package.


188-194: LGTM - Formatting improvement.

The reformatted ternary chain improves readability while maintaining the same protocol selection logic.


300-334: Good defensive improvements to token validation.

The changes properly handle edge cases:

  1. Missing/empty token when Authorization header exists but extractBearerToken returns undefined (e.g., "Bearer " or "Basic xxx")
  2. Defensive null check for this.scope.auth before accessing its properties

This prevents potential runtime errors and provides consistent 401 responses for invalid authentication scenarios.


336-367: LGTM - Verification logic.

The mode-based verification correctly routes:

  • Transparent mode → remote provider's JWKS via verifyTransparentToken
  • Public/orchestrated modes → local gateway keys via verifyGatewayToken

Proper error handling with 401 response on verification failure.


369-408: LGTM - Execute stages.

The flow plan ensures verifyIfJwt completes before these execute stages run. Since verifyIfJwt responds early for missing/invalid tokens, the required.token access in parseSessionHeader and buildAuthorizedOutput is safe.

@frontegg-david frontegg-david merged commit 55e6e2c into main Jan 13, 2026
25 checks passed
@frontegg-david frontegg-david deleted the split-auth-package branch January 13, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants