Skip to content

Commit 706fd16

Browse files
amikofalvyclaudegithub-actions[bot]
authored
feat: Authenticated chat sessions with asymmetric key verification (#2827)
* [US-001] Add PublicKeyConfig and WebClientAuthConfig schemas Extend WebClientConfigSchema with optional auth block supporting asymmetric public key configuration for authenticated chat sessions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [US-002] Add public key validation utility validatePublicKey validates PEM format, rejects private keys, checks algorithm/key-type match, and enforces minimum key sizes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [US-003] Add public key management API endpoints Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [US-004] Add asymmetric JWT verification tests for app credential auth Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [US-005] Persist authenticated user identity and metadata on conversations Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [US-006] Add public key management UI to app edit page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * [US-007] Create global playground app via seed script Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address local review findings for authenticated chat sessions - Critical: require exp claim in asymmetric JWT verification (was optional) - Major: JWT agentId claim takes precedence over header for global apps - Major: fail hard if tenant-scoped app has null tenantId/projectId - Major: add runtime typeof guards for JWT claim extraction - Major: show error toast on key fetch failure (was silent) - Major: rename authMethod to app_credential_web_client_authenticated - Minor: generic error message for JWT claim validation (no detail leakage) - Minor: add error logging to catch blocks in JWT verification - Minor: add aria-label to delete key button - Minor: fix operationId to use create- prefix - Minor: use Apps tag instead of App Auth - Update openapi snapshot for operationId change Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove review artifacts from git, add to gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review comments 1. Move inline schemas (AddPublicKeyRequest, PublicKeyListResponse, PublicKeyResponse) from route file to validation/schemas.ts 2. Create dedicated DAL functions (getAppAuthKeysForProject, updateAppAuthKeysForProject) that fetch only config+type instead of full app record 3. Track third-party userIds as metadata.externalUserId instead of overwriting conversations.userId (reserved for Inkeep user IDs) 4. Replace inline type cast in runAuth.ts with imported WebClientConfig type (Zod-inferred) - Update openapi snapshot Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review comments 1. Move inline schemas to validation/schemas.ts 2. Create dedicated DAL functions (getAppAuthKeysForProject, updateAppAuthKeysForProject) for performant fetches 3. Track third-party userIds as metadata.externalUserId (preserve conversations.userId for Inkeep user IDs) 4. Replace inline type cast with imported WebClientConfig type Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add pr-diff to root gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove pr-context from gitignore, keep only pr-diff Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: migrate playground to app-credential auth path - Update playground token endpoint to sign JWTs in app-credential format (kid: 'playground-rsa', claims: tid/pid/agentId instead of tenantId/projectId) - Return appId in playground token response - Update chat widget to use x-inkeep-app-id header with global playground app - Falls back to legacy tenant/project headers if appId not returned - Token now verified via tryAppCredentialAuth → global app → SpiceDB instead of the bespoke tryTempJwtAuth path Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: skip PoW for authenticated sessions PoW is an anti-abuse mechanism for anonymous sessions. Authenticated sessions are gated by the customer's JWT — PoW is unnecessary and blocks the playground which sets shouldBypassCaptcha: true. Move PoW check into the anonymous-only branch of tryAppCredentialAuth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: unify authenticated app auth — treat all apps the same - Remove SpiceDB canUseProjectStrict check from global app auth path - Token issuer (e.g., manage-ui) is the authorization gate, not agents-api - Global apps resolve scope from token claims (tid/pid/agentId), trusted - Tenant-scoped apps resolve scope from app record, same as before - sub claim is always externalUserId, never used for Inkeep authz - Both paths return through a single unified code path Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add verifiedClaims propagation and validateScopeClaims Identity model redesign (specs/2026-03-26-identity-model-redesign): - Extract non-standard JWT claims as verifiedClaims in tryAppCredentialAuth - Enforce 1KB size limit on verified claims (reject tokens exceeding it) - Add verifiedClaims to BaseExecutionContext.metadata (runtime access) - Persist verifiedClaims to conversations.metadata (analytics) - Keep verifiedClaims strictly separate from unverified userProperties - Add validateScopeClaims flag to WebClientAuthConfig - When enabled, global apps validate tid/pid via SpiceDB canUseProjectStrict - Update playground app seed to set validateScopeClaims: true Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: formatting fixes from Biome Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract buildConversationMetadata helper Replace inscrutable nested ternary spread in chat.ts and chatDataStream.ts with a readable helper function that builds conversation metadata from the execution context and user properties. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: Biome formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: proper error codes for auth failures + allowAnonymous flag - Replace HTTPException(401) with createApiError({ code: 'unauthorized' }) for authenticated session errors — fixes misleading "internal_server_error" code on 401 responses - Add allowAnonymous flag to WebClientAuthConfig (default: true) Apps with auth keys configured can still serve anonymous sessions When false, anonymous fallback is blocked and auth is required - Restructure tryAppCredentialAuth to fall through to anonymous path when asymmetric verification fails and allowAnonymous is true Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add dual-mode tests + fix remaining error formatting - Add tests for anonymous fallback when auth keys are configured - Add test for allowAnonymous: false rejecting anonymous tokens - Add test for authenticated tokens on apps with allowAnonymous: true - Add test verifying error responses use 'unauthorized' code not 'internal_server_error' - Fix remaining HTTPException(403/500) in authenticated path to use createApiError Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: Biome formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: widen app edit dialog, prevent horizontal scroll - Increase dialog from max-w-lg (512px) to max-w-2xl (672px) - Add overflow-x-hidden to prevent horizontal scroll - Add break-all and whitespace-pre-wrap on PEM textarea Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: show public key on hover, copy to clipboard on click Public keys are not secrets — show the full PEM in a tooltip on hover and copy to clipboard when clicking the key row. Adds a small copy icon as affordance. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fail hard with 401 when auth-configured app gets invalid token When an app has auth keys configured and both asymmetric and anonymous JWT verification fail, throw a hard 401 error instead of returning null (which lets dev mode create a default context with test-project). Prevents leaked test-project/test-tenant values from reaching downstream handlers and producing misleading 404 errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: Biome formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update global app tests for opt-in SpiceDB validation - Test 1: global app without validateScopeClaims should NOT call canUseProjectStrict (SpiceDB validation is opt-in) - Test 2: add validateScopeClaims: true to test SpiceDB denial behavior specifically for apps that opt in Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: update OpenAPI snapshot --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent ec1b2f7 commit 706fd16

File tree

31 files changed

+3255
-40
lines changed

31 files changed

+3255
-40
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET=test-bypass-secret-for-ci
108108
# Temporary JWT Keys for Playground (generate with scripts/generate-jwt-keys.sh)
109109
# INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=
110110
# INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY=
111+
# Global playground app ID (created by db:auth:init, defaults to app_playground)
112+
# INKEEP_PLAYGROUND_APP_ID=app_playground
111113

112114
# ========== ANALYTICS TOKENS ================
113115
# PUBLIC_POSTHOG_KEY=

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,5 @@ progress.txt
124124
.pnpm-store/
125125
.vercel
126126

127-
packages/agents-ui-local
127+
packages/agents-ui-local
128+
.claude/pr-diff/

0 commit comments

Comments
 (0)