Commit a929847
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- .changeset
- .github/workflows
- agents-api
- __snapshots__
- src
- __tests__
- manage/routes/crud
- middleware
- run/routes
- domains
- manage/routes
- run/routes
- middleware
- startup
- agents-docs/content/talk-to-your-agents/(chat-components)
- agents-manage-ui
- cypress/e2e
- src
- components
- apps
- form
- form/__tests__/__screenshots__/form.browser.test.tsx
- lib/actions
- packages/agents-core
- drizzle/runtime
- meta
- src
- auth
- data-access/__tests__
- types
- validation
- __tests__
- specs/enforce-app-auth
31 files changed
+970
-288
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
162 | 162 | | |
163 | 163 | | |
164 | 164 | | |
165 | | - | |
| 165 | + | |
166 | 166 | | |
167 | 167 | | |
168 | 168 | | |
| |||
337 | 337 | | |
338 | 338 | | |
339 | 339 | | |
340 | | - | |
| 340 | + | |
341 | 341 | | |
342 | 342 | | |
343 | 343 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10574 | 10574 | | |
10575 | 10575 | | |
10576 | 10576 | | |
10577 | | - | |
10578 | | - | |
10579 | | - | |
10580 | | - | |
10581 | | - | |
10582 | | - | |
10583 | | - | |
10584 | | - | |
10585 | | - | |
10586 | | - | |
10587 | | - | |
10588 | | - | |
10589 | | - | |
10590 | | - | |
10591 | | - | |
10592 | | - | |
10593 | | - | |
10594 | | - | |
10595 | | - | |
10596 | | - | |
10597 | | - | |
10598 | 10577 | | |
10599 | 10578 | | |
10600 | 10579 | | |
| |||
10605 | 10584 | | |
10606 | 10585 | | |
10607 | 10586 | | |
| 10587 | + | |
| 10588 | + | |
| 10589 | + | |
| 10590 | + | |
10608 | 10591 | | |
10609 | 10592 | | |
10610 | 10593 | | |
| |||
10614 | 10597 | | |
10615 | 10598 | | |
10616 | 10599 | | |
10617 | | - | |
10618 | | - | |
| 10600 | + | |
| 10601 | + | |
| 10602 | + | |
| 10603 | + | |
| 10604 | + | |
| 10605 | + | |
| 10606 | + | |
| 10607 | + | |
| 10608 | + | |
10619 | 10609 | | |
10620 | 10610 | | |
10621 | 10611 | | |
| |||
10640 | 10630 | | |
10641 | 10631 | | |
10642 | 10632 | | |
| 10633 | + | |
| 10634 | + | |
| 10635 | + | |
| 10636 | + | |
10643 | 10637 | | |
10644 | 10638 | | |
10645 | 10639 | | |
| |||
10649 | 10643 | | |
10650 | 10644 | | |
10651 | 10645 | | |
10652 | | - | |
10653 | | - | |
| 10646 | + | |
| 10647 | + | |
| 10648 | + | |
| 10649 | + | |
| 10650 | + | |
| 10651 | + | |
| 10652 | + | |
| 10653 | + | |
| 10654 | + | |
10654 | 10655 | | |
10655 | 10656 | | |
10656 | 10657 | | |
| |||
Lines changed: 189 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
36 | 39 | | |
37 | | - | |
| 40 | + | |
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
| |||
191 | 194 | | |
192 | 195 | | |
193 | 196 | | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
194 | 382 | | |
195 | 383 | | |
196 | 384 | | |
| |||
0 commit comments