Skip to content

Commit a929847

Browse files
amikofalvyclaudegithub-actions[bot]
authored
Add Require Authentication toggle for web client apps (#2913)
* Add Require Authentication toggle for web client apps Surfaces the existing allowAnonymous config field in the manage UI, allowing builders to enforce JWT authentication for app access. When enabled, anonymous fallback is blocked and users must present a valid signed JWT. - Add PATCH /auth/keys/settings endpoint for updating auth settings with server-side config merging (preserves existing keys/audience) - Add updateAppAuthSettings API client and server action - Add Require Authentication switch to AuthKeysSection (visible only when public keys are configured) - Pass allowAnonymous prop from AppUpdateForm to AuthKeysSection - Add 5 integration tests for the new settings endpoint Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Update app credentials docs with Require Authentication toggle Document the new Require Authentication toggle in the app edit dialog, update the Dual-mode section header to Enforcing authentication, and mention the toggle in the key management and security model sections. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add changesets for enforce-app-auth feature Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fixup! local-review: address findings (pass 1) * fixup! local-review: baseline (pre-review state) * Fix: revert auth spread destructure that caused data loss The Phase 5 review fix that stripped allowAnonymous and publicKeys from the auth spread before form submit caused silent data loss — the PATCH endpoint does full JSONB column replacement, so stripped fields are erased from the database. Revert to the original spread pattern that preserves all auth fields from the app prop. Also deduplicate toggle description in docs (cross-reference instead). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fixup! local-review: address findings (pass 1) * Remove generated pr-context skill from PR This file is a review infrastructure artifact generated by the local review script, not part of the feature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Default allowAnonymous to false and enforce across all auth paths - Change allowAnonymous schema default from optional (implicit true) to explicit default(false) — new apps require authentication by default - Update 0027 seed migration to include allowAnonymous: false for the playground app - Update playground init code to set allowAnonymous: false when keys are configured - Block anonymous session endpoint (/apps/{appId}/anonymous-session) when allowAnonymous is false — previously minted tokens regardless - Enforce allowAnonymous in runAuth.ts even when no public keys are configured — previously only checked inside the hasAuthConfigured branch, so empty keys silently allowed anonymous access - Default allowAnonymous to false in key add/delete route handlers when spreading existing auth config from DB (old records may lack the field) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Move allowAnonymous to app PATCH with config merge Remove the dedicated PATCH /auth/keys/settings endpoint — allowAnonymous is an app-level setting, not a key-level operation. Instead, add server-side config merge to the app update handler: when a web_client config is PATCHed, the handler fetches the existing app and deep-merges auth fields (preserving publicKeys from the dedicated key endpoints). This lets callers send partial auth config (e.g., just allowAnonymous) without losing keys, audience, or other fields. - Remove settings endpoint from appAuthKeys.ts - Remove updateAppAuthSettings from API client - Add config merge logic to app update handler in apps.ts - Update server action to call updateApp with config body - Pass allowedDomains through to satisfy Zod validation - Update tests to use app PATCH endpoint - Update docs API reference Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Update changeset message for agents-api Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix dialog close on key delete; show toggle unconditionally - Remove revalidatePath from key add/delete actions — the AuthKeysSection manages its own state via loadKeys(), and revalidatePath was causing the page server component to re-render, unmounting the edit dialog - Show the Require Authentication toggle always, not just when keys are configured — there are valid cases where you want to lock down an app before the public key is ready Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove all revalidatePath calls from auth key/settings actions Every revalidatePath call in this file caused the edit dialog to close by triggering a server component re-render that unmounted the client component tree. All state in this section is managed locally — the component refetches via loadKeys() and the toggle uses optimistic state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Unify create and edit app forms with batched save Refactor AuthKeysSection into a controlled component — no server calls, just local state. The parent form owns all pending changes (keys to add, keys to delete, requireAuth toggle) and orchestrates API calls in order on submit: create/update app → delete keys → add keys. Both create and edit forms now look identical: - Name, description, default agent, allowed domains, prompt - Separator - Auth keys section (add/remove keys, require auth toggle) - Audience field - Submit button Nothing saves until the submit button is pressed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Make default agent required in create and edit app forms UI-only validation — the API still accepts apps without a default agent, but the form enforces selection to prevent misconfigured apps. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add comprehensive allowAnonymous enforcement tests Anonymous session endpoint: - Rejects when allowAnonymous is false (401) - Allows when allowAnonymous is true - Allows when allowAnonymous is not set (default behavior) - Correctly toggles: reject → allow after switching back to true Key operations preserve allowAnonymous: - Adding a key preserves existing allowAnonymous value - Deleting a key preserves existing allowAnonymous value Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review findings: migration, warning banner, dead code - Add migration 0030 to backfill allowAnonymous=true for all existing web_client apps (preserves current behavior) and set playground to false - Show warning banner when Require Authentication is enabled but no public keys are configured - Show toggle unconditionally (user requirement) with contextual warning - Remove unused requireAuth field from form validation schema - Always call onAppUpdated() after successful app PATCH even on partial key failure so parent refreshes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Default Require Authentication toggle to ON Create form defaults to true (matching schema default of allowAnonymous: false). Edit form uses !== true so undefined/false both show the toggle as ON — only explicitly set allowAnonymous: true turns it off. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Mark Key ID and Public Key fields as required in add key form Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Mark Algorithm field as required in add key form Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove generated pr-context skill from PR Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Use allowlist for auth config merge instead of denylist Only allow allowAnonymous and audience to be updated through the app PATCH config merge. Previously the merge stripped publicKeys but allowed any other field through, which could let an admin inject validateScopeClaims: false to bypass SpiceDB authorization checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Validate incoming web client config through Zod schema Parse incoming config through WebClientConfigSchema before merging to strip any unexpected fields. Combined with the allowlist merge (only allowAnonymous and audience are updatable), this ensures: - Schema validation catches malformed config early - No extra fields can be injected through the merge path - publicKeys and validateScopeClaims remain managed by dedicated routes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Flatten auth config into webClient and bump changesets to minor Remove the unnecessary auth nesting — publicKeys, audience, validateScopeClaims, and allowAnonymous now live directly on webClient instead of webClient.auth. Migration 0030 updated to: 1. Hoist fields from webClient.auth to webClient for existing apps 2. Remove the auth key 3. Clean up null-valued hoisted fields 4. Backfill allowAnonymous=true for existing apps 5. Set playground to allowAnonymous=false Also: - Import ALLOWED_DOMAIN_PATTERN from agents-core/client-exports instead of duplicating it in the manage UI validation - Bump both changesets from patch to minor (includes migration) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove validateScopeClaims — always validate scope for global apps There's no valid reason to skip SpiceDB scope validation on global apps. If a global app accepts tenant/project claims from JWTs, those claims must always be verified. - Remove validateScopeClaims from WebClientConfigSchema and response schema - Remove from WebClientAuthConfigSchema (kept for backward compat type) - Make scope validation unconditional in runAuth.ts for global apps - Remove from playground seed migration and init code - Migration 0030 strips the field from existing apps - Update test assertion to expect validation call Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove WebClientAuthConfigSchema and validateScopeClaims from config schemas Complete cleanup: remove the old nested auth schema entirely and strip validateScopeClaims from both WebClientConfigSchema and response schema. Remove associated type export and tests. Add test verifying validateScopeClaims is stripped by Zod parsing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: update OpenAPI snapshot * Fix docs: auth is required by default, not anonymous by default Update Enforcing authentication section to reflect that new apps require auth by default. Remove inline API example. Update security model table and key management cross-reference. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Make allowAnonymous required with default(false) in schema Changed from optional() to default(false) so Zod enforces the field is always present. Tests updated to explicitly set allowAnonymous: true when anonymous access is needed. OpenAPI snapshot updated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Clean up dead code and improve readability - Remove onKeysChange prop and no-op call from AuthKeysSection - Extract allowAnonymous = !requireAuth into named variable in both forms for clarity - Remove onKeysChange from parent form call sites Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Revert 0027 migration to match origin/main The lineage checker requires applied migrations to be unchanged. Migration 0030 already handles stripping validateScopeClaims from existing apps including the playground. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CI: add required publicKeys/allowAnonymous to all app config usages The schema now requires publicKeys and allowAnonymous (non-optional with default). Update init.ts to always provide both fields, and update data-access tests to include them in config objects. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix Cypress E2E: add allowAnonymous to test app config Cypress tests create web_client apps that need anonymous session access. With allowAnonymous now defaulting to false, they need to explicitly opt in. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix remaining test assertions for required allowAnonymous/publicKeys - Update createApp assertion to include publicKeys and allowAnonymous - Update schema test: omitted fields now get defaults ([] and false) instead of being undefined Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Pin playwright to 1.58.2 to fix CI browser version mismatch The caret range ^1.58.2 allowed version drift between the cached Playwright browsers and the installed package version. CI caches browsers keyed on the lockfile hash, but the fallback restore-key could restore browsers from a different Playwright version, causing "Executable doesn't exist" errors. See: microsoft/playwright#39122 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CI: use project-local playwright for browser install pnpx resolves to the latest global Playwright version, which downloads browsers for a different build than the project's pinned version. Switch to pnpm exec to use the project-local playwright@1.58.2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CI: scope playwright install to agents-manage-ui package pnpm exec at root can't find playwright — it's a devDep of agents-manage-ui, not the root workspace. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- 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 dcbdb98 commit a929847

File tree

31 files changed

+970
-288
lines changed

31 files changed

+970
-288
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@inkeep/agents-api": minor
3+
---
4+
5+
Add server-side config merge for web client app updates, enforce allowAnonymous across all auth paths, flatten auth config into webClient, and add migration to backfill allowAnonymous for existing apps
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@inkeep/agents-manage-ui": minor
3+
---
4+
5+
Add Require Authentication toggle for web client apps

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ jobs:
162162
APP_TOKEN: ${{ steps.app-token.outputs.token }}
163163

164164
- name: Install Playwright
165-
run: pnpx playwright install chromium --with-deps # install browsers + dependencies for Chromium only
165+
run: pnpm --filter @inkeep/agents-manage-ui exec playwright install chromium --with-deps # install browsers + dependencies for Chromium only
166166

167167
# Clean database files before running tests
168168
- name: Clean database files
@@ -337,7 +337,7 @@ jobs:
337337
restore-keys: playwright-${{ runner.os }}-
338338

339339
- name: Install Playwright
340-
run: pnpx playwright install chromium --with-deps
340+
run: pnpm --filter @inkeep/agents-manage-ui exec playwright install chromium --with-deps
341341

342342
- name: Build create-agents and dependencies
343343
run: |

agents-api/__snapshots__/openapi.json

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10574,27 +10574,6 @@
1057410574
"example": "user_123",
1057510575
"type": "string"
1057610576
},
10577-
"WebClientAuthConfig": {
10578-
"properties": {
10579-
"allowAnonymous": {
10580-
"type": "boolean"
10581-
},
10582-
"audience": {
10583-
"type": "string"
10584-
},
10585-
"publicKeys": {
10586-
"default": [],
10587-
"items": {
10588-
"$ref": "#/components/schemas/PublicKeyConfig"
10589-
},
10590-
"type": "array"
10591-
},
10592-
"validateScopeClaims": {
10593-
"type": "boolean"
10594-
}
10595-
},
10596-
"type": "object"
10597-
},
1059810577
"WebClientConfig": {
1059910578
"properties": {
1060010579
"type": {
@@ -10605,6 +10584,10 @@
1060510584
},
1060610585
"webClient": {
1060710586
"properties": {
10587+
"allowAnonymous": {
10588+
"default": false,
10589+
"type": "boolean"
10590+
},
1060810591
"allowedDomains": {
1060910592
"items": {
1061010593
"minLength": 1,
@@ -10614,8 +10597,15 @@
1061410597
"minItems": 1,
1061510598
"type": "array"
1061610599
},
10617-
"auth": {
10618-
"$ref": "#/components/schemas/WebClientAuthConfig"
10600+
"audience": {
10601+
"type": "string"
10602+
},
10603+
"publicKeys": {
10604+
"default": [],
10605+
"items": {
10606+
"$ref": "#/components/schemas/PublicKeyConfig"
10607+
},
10608+
"type": "array"
1061910609
}
1062010610
},
1062110611
"required": [
@@ -10640,6 +10630,10 @@
1064010630
},
1064110631
"webClient": {
1064210632
"properties": {
10633+
"allowAnonymous": {
10634+
"default": false,
10635+
"type": "boolean"
10636+
},
1064310637
"allowedDomains": {
1064410638
"items": {
1064510639
"minLength": 1,
@@ -10649,8 +10643,15 @@
1064910643
"minItems": 1,
1065010644
"type": "array"
1065110645
},
10652-
"auth": {
10653-
"$ref": "#/components/schemas/WebClientAuthConfig"
10646+
"audience": {
10647+
"type": "string"
10648+
},
10649+
"publicKeys": {
10650+
"default": [],
10651+
"items": {
10652+
"$ref": "#/components/schemas/PublicKeyConfig"
10653+
},
10654+
"type": "array"
1065410655
}
1065510656
},
1065610657
"required": [

agents-api/src/__tests__/manage/routes/crud/appAuthKeys.test.ts

Lines changed: 189 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ describe('App Auth Keys Routes', () => {
3333
return body.data.app;
3434
};
3535

36+
const appUrl = (tenantId: string, projectId: string, appId: string) =>
37+
`/manage/tenants/${tenantId}/projects/${projectId}/apps/${appId}`;
38+
3639
const keysUrl = (tenantId: string, projectId: string, appId: string) =>
37-
`/manage/tenants/${tenantId}/projects/${projectId}/apps/${appId}/auth/keys`;
40+
`${appUrl(tenantId, projectId, appId)}/auth/keys`;
3841

3942
describe('POST /auth/keys', () => {
4043
it('should add a public key to an app', async () => {
@@ -191,6 +194,191 @@ describe('App Auth Keys Routes', () => {
191194
});
192195
});
193196

197+
describe('PATCH /apps/:id (allowAnonymous via app update)', () => {
198+
it('should set allowAnonymous via app PATCH config merge', async () => {
199+
const tenantId = await createTestTenantWithOrg('auth-settings-merge');
200+
const projectId = 'default-project';
201+
await createTestProject(manageDbClient, tenantId, projectId);
202+
const app = await createTestApp(tenantId, projectId);
203+
204+
const res = await makeRequest(appUrl(tenantId, projectId, app.id), {
205+
method: 'PATCH',
206+
body: JSON.stringify({
207+
config: {
208+
type: 'web_client',
209+
webClient: { allowedDomains: ['*'], allowAnonymous: true },
210+
},
211+
}),
212+
});
213+
214+
expect(res.status).toBe(200);
215+
const getRes = await makeRequest(appUrl(tenantId, projectId, app.id));
216+
const appBody = await getRes.json();
217+
expect(appBody.data.config.webClient.allowAnonymous).toBe(true);
218+
});
219+
220+
it('should preserve existing keys when updating allowAnonymous via app PATCH', async () => {
221+
const tenantId = await createTestTenantWithOrg('auth-settings-preserve');
222+
const projectId = 'default-project';
223+
await createTestProject(manageDbClient, tenantId, projectId);
224+
const app = await createTestApp(tenantId, projectId);
225+
226+
const pem = await rsaPem();
227+
await makeRequest(keysUrl(tenantId, projectId, app.id), {
228+
method: 'POST',
229+
body: JSON.stringify({ kid: 'preserved-key', publicKey: pem, algorithm: 'RS256' }),
230+
});
231+
232+
await makeRequest(appUrl(tenantId, projectId, app.id), {
233+
method: 'PATCH',
234+
body: JSON.stringify({
235+
config: {
236+
type: 'web_client',
237+
webClient: { allowedDomains: ['*'], allowAnonymous: true },
238+
},
239+
}),
240+
});
241+
242+
const listRes = await makeRequest(keysUrl(tenantId, projectId, app.id));
243+
const body = await listRes.json();
244+
expect(body.data).toHaveLength(1);
245+
expect(body.data[0].kid).toBe('preserved-key');
246+
});
247+
248+
it('should preserve audience when updating allowAnonymous via app PATCH', async () => {
249+
const tenantId = await createTestTenantWithOrg('auth-settings-audience');
250+
const projectId = 'default-project';
251+
await createTestProject(manageDbClient, tenantId, projectId);
252+
const app = await createTestApp(tenantId, projectId);
253+
254+
await makeRequest(appUrl(tenantId, projectId, app.id), {
255+
method: 'PATCH',
256+
body: JSON.stringify({
257+
config: {
258+
type: 'web_client',
259+
webClient: {
260+
allowedDomains: ['example.com'],
261+
audience: 'https://my-app.example.com',
262+
},
263+
},
264+
}),
265+
});
266+
267+
await makeRequest(appUrl(tenantId, projectId, app.id), {
268+
method: 'PATCH',
269+
body: JSON.stringify({
270+
config: {
271+
type: 'web_client',
272+
webClient: {
273+
allowedDomains: ['example.com'],
274+
allowAnonymous: true,
275+
},
276+
},
277+
}),
278+
});
279+
280+
const getRes = await makeRequest(appUrl(tenantId, projectId, app.id));
281+
const appBody = await getRes.json();
282+
expect(appBody.data.config.webClient.audience).toBe('https://my-app.example.com');
283+
expect(appBody.data.config.webClient.allowAnonymous).toBe(true);
284+
});
285+
});
286+
287+
describe('PATCH /apps/:id strips publicKeys from payload', () => {
288+
it('should not overwrite existing keys when publicKeys is sent in PATCH body', async () => {
289+
const tenantId = await createTestTenantWithOrg('auth-settings-strip-keys');
290+
const projectId = 'default-project';
291+
await createTestProject(manageDbClient, tenantId, projectId);
292+
const app = await createTestApp(tenantId, projectId);
293+
294+
const pem = await rsaPem();
295+
await makeRequest(keysUrl(tenantId, projectId, app.id), {
296+
method: 'POST',
297+
body: JSON.stringify({ kid: 'real-key', publicKey: pem, algorithm: 'RS256' }),
298+
});
299+
300+
const res = await makeRequest(appUrl(tenantId, projectId, app.id), {
301+
method: 'PATCH',
302+
body: JSON.stringify({
303+
config: {
304+
type: 'web_client',
305+
webClient: {
306+
allowedDomains: ['example.com'],
307+
publicKeys: [],
308+
allowAnonymous: true,
309+
},
310+
},
311+
}),
312+
});
313+
expect(res.status).toBe(200);
314+
315+
const listRes = await makeRequest(keysUrl(tenantId, projectId, app.id));
316+
const body = await listRes.json();
317+
expect(body.data).toHaveLength(1);
318+
expect(body.data[0].kid).toBe('real-key');
319+
});
320+
});
321+
322+
describe('Key operations preserve allowAnonymous', () => {
323+
it('should preserve allowAnonymous when adding a key', async () => {
324+
const tenantId = await createTestTenantWithOrg('auth-keys-preserve-anon-add');
325+
const projectId = 'default-project';
326+
await createTestProject(manageDbClient, tenantId, projectId);
327+
const app = await createTestApp(tenantId, projectId);
328+
329+
await makeRequest(appUrl(tenantId, projectId, app.id), {
330+
method: 'PATCH',
331+
body: JSON.stringify({
332+
config: {
333+
type: 'web_client',
334+
webClient: { allowedDomains: ['example.com'], allowAnonymous: true },
335+
},
336+
}),
337+
});
338+
339+
const pem = await rsaPem();
340+
await makeRequest(keysUrl(tenantId, projectId, app.id), {
341+
method: 'POST',
342+
body: JSON.stringify({ kid: 'new-key', publicKey: pem, algorithm: 'RS256' }),
343+
});
344+
345+
const getRes = await makeRequest(appUrl(tenantId, projectId, app.id));
346+
const appBody = await getRes.json();
347+
expect(appBody.data.config.webClient.allowAnonymous).toBe(true);
348+
});
349+
350+
it('should preserve allowAnonymous when deleting a key', async () => {
351+
const tenantId = await createTestTenantWithOrg('auth-keys-preserve-anon-del');
352+
const projectId = 'default-project';
353+
await createTestProject(manageDbClient, tenantId, projectId);
354+
const app = await createTestApp(tenantId, projectId);
355+
356+
const pem = await rsaPem();
357+
await makeRequest(keysUrl(tenantId, projectId, app.id), {
358+
method: 'POST',
359+
body: JSON.stringify({ kid: 'del-key', publicKey: pem, algorithm: 'RS256' }),
360+
});
361+
362+
await makeRequest(appUrl(tenantId, projectId, app.id), {
363+
method: 'PATCH',
364+
body: JSON.stringify({
365+
config: {
366+
type: 'web_client',
367+
webClient: { allowedDomains: ['example.com'], allowAnonymous: true },
368+
},
369+
}),
370+
});
371+
372+
await makeRequest(`${keysUrl(tenantId, projectId, app.id)}/del-key`, {
373+
method: 'DELETE',
374+
});
375+
376+
const getRes = await makeRequest(appUrl(tenantId, projectId, app.id));
377+
const appBody = await getRes.json();
378+
expect(appBody.data.config.webClient.allowAnonymous).toBe(true);
379+
});
380+
});
381+
194382
describe('DELETE /auth/keys/:kid', () => {
195383
it('should delete a key by kid', async () => {
196384
const tenantId = await createTestTenantWithOrg('auth-keys-delete');

0 commit comments

Comments
 (0)