Repository navigation
feat: add viewerRequestFunctionCode to run code on every viewer request - #311
Conversation
NextjsGlobalFunctions owns the viewer-request CloudFront Function on its dynamic and image behaviors, and rejects overrides that claim that event type, which left HTTP Basic auth and similar edge gates with nowhere to go. The new prop on NextjsGlobalFunctions and NextjsGlobalContainers is spliced ahead of cdk-nextjs's own logic, and runs on every behavior cdk-nextjs adds, static ones included. Closes #310
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…onal KeyValueStore Functions the hook runs in move to cloudfront-js-2.0 with an async handler that awaits viewerRequest, so user code can be async and read a store through cf.kvs(). The new viewerRequestKeyValueStore prop is associated with each of them, keeping credentials out of the template. Without the hook, cdk-nextjs's own function stays on cloudfront-js-1.0.
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
The x-forwarded-host/redirect function and the assetPrefix rewrite now run on cloudfront-js-2.0 with async handlers whether or not viewerRequestFunctionCode is set, so there is one runtime and one handler shape. The ES5.1 code runs unchanged; existing deployments see an in-place update of those functions.
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
# Conflicts: # README.md
- R1-1: refuse user code that assigns `handler` (var/let/const/class, bare or globalThis/this assignment), not just a `function handler` declaration - R1-2: accept `viewerRequest` defined as `var`/`let`/`const` - R1-3: wrap `kvs.get` in try/catch in the JSDoc example, as the README does - R1-4: move the CloudFrontFn notes out of the deployed code so they don't count toward the 10 KB limit - R1-5: share one pass-through CloudFront Function between the dynamic and static behaviors - R1-6: render and check `viewerRequestFunctionCode` once, in the constructor - R1-7: route the assetPrefix viewer-request check through assertViewerRequestUnclaimed
- R2-1: rename cdk-nextjs's handler when user code shares the file and end it with `handler = cdkNextjsHandler`, so no top-level assignment (var, destructuring, globalThis["handler"]) can replace it - R2-2: refuse only `handler` declarations (function, let/const/class); var and assignments are now harmless, so locals using them pass - R2-3: refuse a `function* handler` declaration - R2-4: accept `viewerRequest` declared after another variable in one var/let/const - R2-5: missing-viewerRequest error names the var/let/const forms - R2-6: prop JSDocs (and the generated struct) document the const form - R2-7: shared pass-through test checks every behavior references the one function - R2-8: gate the pass-through on the rendered code, not the prop
- R3-1/R3-4/R3-5/R3-6: drop the renamed handler + trailing reassignment (a user `cdkNextjsHandler` assignment still replaced ours, and a user `var handler` beside our `function handler` is a SyntaxError if the file parses as a module); keep `async function handler` and refuse any use of the name `handler` in user code instead - R3-2: comma-list and destructured `let`/`const handler` are refused (by the same check) - R3-3: the viewerRequest check no longer accepts `===` comparisons or member assignments - R3-7: root-construct JSDocs name var/let/const
- R4-1: refuse `overrides.distributionProps.defaultBehavior` when `viewerRequestFunctionCode` is set, since it replaces the default behavior and drops the hook from pages and API routes - R4-2: warn instead of size-checking when the combined code contains an unresolved CDK token - R4-3: README `put-key` strips the newlines GNU `base64` wraps with - R4-4: README and JSDoc Basic-auth examples delete the verified `authorization` header so it isn't forwarded to the origin - R4-5: dedent cdk-nextjs's own handler code so its indentation doesn't count toward the 10 KB limit - S4-4: test the Global Containers dynamic/image viewer-request override error
- R5-1: allow `overrides.distributionProps.defaultBehavior` under a `basePath`, where cdk-nextjs's own behaviors serve the app - R5-2: dedent cdk-nextjs's handler after the hook is spliced in, so the hook's indentation doesn't count toward 10 KB either - S5-3: README notes code with a CDK token gets a warning, not a size check
Review to convergence: 5 roundsEach round, fresh reviewers checked for bugs (
FixesUser code replacing cdk-nextjs's Coverage
Validation
Size budget
Functions
Docs and examples
Escalations, all resolved by the author: keep as is
|
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…tion the code is copied into
…JSDoc states the contract and links to it
…udfront-js-2.0 supports
…Code splicing to utils/viewer-request-functions
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Closes #310.
Problem
NextjsGlobalFunctionsputs its own CloudFront Function on viewer-request for the dynamic and_next/image*behaviors (overwritesX-Forwarded-Host, redirects repeated slashes), and since 0.7 an override claiming viewer-request throws at synth. That left HTTP Basic auth (and geo-gating, etc.) with no supported home:Authorization(OAC signs the Function URL request with it), and_next/static/public/never reach the server.Change
New
viewerRequestFunctionCode?: FunctionCodeprop onNextjsGlobalFunctions,NextjsGlobalContainersandNextjsDistribution. The code must defineviewerRequest(event), as a function (sync or async) or avar/let/const. It is a CloudFront handler in all but name: return a response to answer the viewer, or the request (or nothing) to carry on.It is spliced into the viewer-request function of every behavior cdk-nextjs adds:
_next/image*(Global Functions)X-Forwarded-Hostoverwrite, and it also runs before the//→ 308 redirectViewerRequestFn(containers needed none before), also used for the static behaviors below_next/static*,public/*ViewerRequestFn)<assetPrefix>/_next/static*Runtime and KeyValueStore:
cloudfront-js-2.0with anasync handler, hook or not. That includes the existingx-forwarded-host/308 function and the assetPrefix rewrite. The hook isawait viewerRequest(event), soviewerRequestcan be sync or async. cdk-nextjs's function code is rewritten in the ES6 the 2.0 runtime documents (const/let, arrow functions; no destructuring or spread). Existing deployments see an in-place update of those functions on the next deploy.viewerRequestKeyValueStore?: IKeyValueStore(same three constructs) is associated with each of those functions.cf.kvs()reads it with no ID, and the user code goes first in the file, which is where CloudFront requiresimport cf from "cloudfront". Credentials live in the store, not in the template. The README shows theput-keycommand. The example wrapskvs.getin try/catch, so a store with nobasic-authkey yet answers 401, and deletes the verifiedauthorizationheader so it isn't forwarded to the origin.Synth-time checks:
viewerRequestand doesn't use the namehandleranywhere, comments and strings included: an assignment would replace cdk-nextjs's handler and alet/const/classwould be a syntax error, and telling those from a harmless local takes a parser;overrides.*BehaviorOptions.functionAssociationsviewer-request entry on a behavior the prop now covers throws, pointing at the prop. The existing Global Functions error now points there too, rather than at viewer-response/middleware;overrides.distributionProps.defaultBehaviorthrows when the prop is set and there's nobasePath, since it would replace the default behavior and its viewer-request function; customize it throughoverrides.dynamicBehaviorOptions.With the prop unset, no function or association is added to static behaviors, and the existing functions differ only in runtime, the
asynckeyword, and having their comments and indentation dropped from the deployed code (they counted toward the 10 KB limit).Code layout:
x-forwarded-host/308, pass-through, assetPrefix rewrite) and the splicing/checks ofviewerRequestFunctionCodelive insrc/utils/viewer-request-functions.ts;NextjsDistributiononly creates and associates the functions. Further extractions fromnextjs-distribution.tsare tracked in refactor: extract pure helpers out of nextjs-distribution.ts #313.Not covered (documented)
distribution's default behavior.Testing
src/nextjs-distribution.test.tsthat run the synthesized functions innode:vm, with a fakecloudfrontmodule standing in forcf.kvs(): 401/pass-through on every behavior for both Global types, one shared pass-through function on Global Containers,viewerRequestas avar/const, auth before the 308,X-Forwarded-Hoststill overwritten, assetPrefix rewrite, credentials read from the store, store association on every hooked function, 2.0 on every function with and without the hook, no added functions when unset, dedented deployed code, the token warning, and each synth error (static and Containers dynamic/image overrides,defaultBehaviorand itsbasePathexemption, store without code, missingviewerRequest, eighthandlerforms, over 10 KB). The existing redirect/x-forwarded-hostsuite now awaits the async handler.src/utils/viewer-request-functions.test.tsfor the handler builders, the splice and the checks.pnpm compile,pnpm projen(regeneratesOptionalNextjsDistributionProps),pnpm eslint,pnpm test: 1176/1176 passing.cf.kvs()with no ID.