feat: auth middleware for createDurablyHandler#76
Conversation
Design for issue #65. Adds authenticate/onTrigger/onRunAccess/scopeRuns hooks to createDurablyHandler with TContext + TLabels generics. Key decisions: - auth nested object with required authenticate - handle() only public method (closure-scoped internals) - authenticate before onRequest (fail fast) - validate before auth hooks (no misleading errors) - RunFilter reused for scopeRuns, separate RunsSubscribeFilter - operation metadata on onRunAccess for fine-grained control Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Breaking change: DurablyHandler now only exposes handle() method. Individual methods (trigger, runs, run, etc.) are removed from the public interface and implemented as closure-scoped helpers. Auth middleware features: - auth.authenticate: runs first, returns TContext, fail fast - auth.onTrigger: guards triggers (after body validation + job lookup) - auth.onRunAccess: guards run operations with operation metadata - auth.scopeRuns: transforms RunFilter for list queries - auth.scopeRunsSubscribe: transforms filter for SSE subscriptions - TLabels inferred from Durably instance for type-safe labels - Response throwing pattern for rejection - Query parameter validation (status, limit, offset) Closes #65 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract withErrorHandling() to deduplicate try/catch in 7 handlers - Extract requireRunAccess() to deduplicate run fetch + auth check in 6 handlers - Pass parsed URL from handle() to handlers instead of re-parsing - Use Pick<RunFilter, ...> for RunsSubscribeFilter instead of duplicating fields - Remove dead else branch in handleRunsSubscribe - Use parseRunsSubscribeFilter in scopeRuns fallback (was parsing unused fields) - Fix: all run-level endpoints now check run existence even without auth - Derive VALID_STATUSES from const array with satisfies for type safety Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- llms.md: rewrite createDurablyHandler section with auth middleware, remove individual handler methods, add AuthConfig/RunOperation types - http-handler.md: replace "Individual Handlers" and "Security Considerations" sections with built-in auth middleware docs - index.md: add missing type exports (AuthConfig, RunOperation, etc.) - Regenerate website/public/llms.txt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds an auth middleware to createDurablyHandler: generic AuthConfig with authenticate, onTrigger, onRunAccess, scopeRuns, scopeRunsSubscribe hooks; refactors handler surface to a single handle(request, basePath) entry; introduces RunOperation and label-generic TriggerRequest and related parsing/authorization flows. Changes
Sequence DiagramsequenceDiagram
actor Client
participant Handler as DurablyHandler
participant Auth as AuthConfig
participant Router as Router
participant Endpoint as EndpointHandler
Client->>Handler: handle(request, basePath)
Handler->>Auth: authenticate(request)
Auth-->>Handler: ctx
Handler->>Handler: onRequest() [optional]
Handler->>Router: determine path & method
alt Trigger
Router->>Endpoint: handleTrigger(triggerRequest)
Endpoint->>Auth: onTrigger(ctx, triggerRequest)
Auth-->>Endpoint: allow / throw Response
Endpoint-->>Handler: Response
else Run-level op (read/subscribe/steps/retry/cancel/delete)
Router->>Endpoint: handleRunOperation(runId,...)
Endpoint->>Auth: requireRunAccess(ctx, run, {operation})
Auth-->>Endpoint: allow / throw Response
Endpoint-->>Handler: Response
else Runs list / subscribe
Router->>Endpoint: handleRuns/handleRunsSubscribe(filter)
Endpoint->>Auth: scopeRuns(scopeRunsSubscribe)(ctx, filter)
Auth-->>Endpoint: scopedFilter
Endpoint-->>Handler: Response / SSE
end
Handler-->>Client: Response / Stream
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
docs/design-auth-middleware.md (1)
263-292: Consider adding a language identifier to the execution flow code block.The ASCII diagram at line 263 uses a fenced code block without a language identifier. While the diagram is readable, adding
textorplaintextas the language would satisfy linter expectations.📝 Suggested fix
-``` +```text handle(request, basePath)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/design-auth-middleware.md` around lines 263 - 292, The fenced ASCII flow diagram starting with "handle(request, basePath)" is missing a language identifier; update the code fence that wraps the diagram to include a plain-text identifier (e.g., "text" or "plaintext") so the linter recognizes it—locate the block containing the diagram lines beginning with "handle(request, basePath)" and replace the opening ``` with ```text (or ```plaintext).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@website/api/http-handler.md`:
- Around line 195-198: The example's TContext is inconsistent: authenticate
returns { orgIds: new Set(...) } but scopeRuns and scopeRunsSubscribe read
ctx.currentOrgId; update the authenticate implementation to return both orgIds
and a currentOrgId (e.g., pick the primary/orgs[0].id or derive from session) so
ctx.currentOrgId exists, or alternatively update scopeRuns and
scopeRunsSubscribe to use ctx.orgIds instead; locate the authenticate function
and the scopeRuns/scopeRunsSubscribe hooks and make the shapes consistent
(ensure TContext includes both orgIds: Set<string> and currentOrgId: string if
you choose the first option).
---
Nitpick comments:
In `@docs/design-auth-middleware.md`:
- Around line 263-292: The fenced ASCII flow diagram starting with
"handle(request, basePath)" is missing a language identifier; update the code
fence that wraps the diagram to include a plain-text identifier (e.g., "text" or
"plaintext") so the linter recognizes it—locate the block containing the diagram
lines beginning with "handle(request, basePath)" and replace the opening ```
with ```text (or ```plaintext).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 98da50b9-f504-4de2-8210-a1db2890ab3f
📒 Files selected for processing (9)
docs/design-auth-middleware.mdpackages/durably/docs/llms.mdpackages/durably/src/index.tspackages/durably/src/server.tspackages/durably/tests/node/core-extensions.test.tspackages/durably/tests/shared/server.shared.tswebsite/api/http-handler.mdwebsite/api/index.mdwebsite/public/llms.txt
- Fix TContext inconsistency in doc examples: simplify to single orgId instead of orgIds Set with mismatched currentOrgId - Add `text` language identifier to ASCII diagram code block in design doc Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/design-auth-middleware.md`:
- Around line 367-369: Update the design doc to remove the stale note about
using request.clone().json(); instead describe that handleTrigger parses the
incoming body once via request.json() and passes the parsed object to onTrigger
(mentioning handleTrigger and onTrigger by name), and explain that onTrigger
therefore receives the parsed body rather than re-reading the request stream so
no cloning or second read is performed.
- Around line 310-316: The runtime currently allows an auth object missing
authenticate (e.g., { onRunAccess() {} }) because handle() only checks if
(auth?.authenticate) and silently skips hooks, so add a validation guard inside
handle() where auth is read: if auth is provided but auth.authenticate is falsy,
throw a clear error (e.g., "auth provided but missing required 'authenticate'
hook") so the process fails-fast; locate the auth usage in handle() and validate
before any auth hooks (onTrigger, onRunAccess, scopeRuns, scopeRunsSubscribe)
are invoked to ensure ctx cannot remain undefined.
In `@packages/durably/docs/llms.md`:
- Around line 378-403: Update the documented TypeScript interfaces to match the
source: change the CreateDurablyHandlerOptions declaration to include the
generic defaults and constraints (TContext = undefined, TLabels extends
Record<string, string> = Record<string, string>) and change AuthConfig to
declare TLabels extends Record<string, string> = Record<string, string>; ensure
the interface names CreateDurablyHandlerOptions and AuthConfig are edited so the
docs reflect the exact signatures used in packages/durably/src/server.ts.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 83778ec9-8758-4d0e-addb-ad5e0ed75d46
📒 Files selected for processing (4)
docs/design-auth-middleware.mdpackages/durably/docs/llms.mdwebsite/api/http-handler.mdwebsite/public/llms.txt
- Add runtime validation: throw if auth provided without authenticate - Fix stale request.clone().json() note in design doc - Add generic defaults/constraints to llms.md type signatures - Update design doc to reflect runtime validation guard - Add test for auth without authenticate Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
authoption tocreateDurablyHandlerfor multi-tenant authentication and authorizationauthenticate→onTrigger→onRunAccess→scopeRuns/scopeRunsSubscribehook chain with typedTContextandTLabelsgenericshandle()method exposed (closure-scoped handlers prevent auth bypass)Closes #65
Changes
d3970fc2f01532AuthConfig,RunOperation,RunsSubscribeFiltertypes, closure-scoped handlersa47cfdbwithErrorHandling,requireRunAccesshelpers, pass parsed URL, fix run existence checks5fbd494Test plan
pnpm validate)handle()routing🤖 Generated with Claude Code
Summary by CodeRabbit