Skip to content

feat(auth0-fastify): dynamic application base URL support#81

Open
frederikprijck wants to merge 12 commits into
mainfrom
worktree-dynamic-app-base-url
Open

feat(auth0-fastify): dynamic application base URL support#81
frederikprijck wants to merge 12 commits into
mainfrom
worktree-dynamic-app-base-url

Conversation

@frederikprijck

@frederikprijck frederikprijck commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

Makes dynamic appBaseUrl a standalone capability in @auth0/auth0-fastify, now accepting three forms:

  • Static string — a single fixed base URL (unchanged behavior).
  • Allow-list (string[]) — base URL is inferred per request and must match one of the listed origins, otherwise the request is rejected with HTTP 500.
  • Omitted — base URL is inferred per request with no restriction.

Key changes:

  • Decoupled from MCD — dynamic/allow-list base URL now works with a static string domain, not only a DomainResolver. The two-arm options union collapses into a single shape (appBaseUrl?: string | string[]).
  • trustProxy-based inference — replaced raw x-forwarded-* header reads with Fastify's request.host/request.protocol, which honor the host app's trustProxy setting. Trust is delegated to the app's Fastify config (the idiomatic equivalent of Express's trust proxy).
  • Secure-cookie anti-downgrade — in dynamic/allow-list mode, session cookies default to secure: true; an explicit secure: false while NODE_ENV === 'production' throws InvalidConfigurationError.
  • Typed error — new InvalidConfigurationError (exported) for invalid config and resolution failures.
  • Base-URL handling extracted into a focused, unit-tested src/app-base-url.ts module.

Behavior change for proxied deployments

Inference now requires Fastify({ trustProxy: true }) for x-forwarded-* headers to be honored (previously they were always trusted). Documented in EXAMPLES.md. A static string domain with no appBaseUrl is now legal (previously threw).

Test Plan

  • npm run test -w @auth0/auth0-fastify — 60 tests pass (17 unit + 43 integration)
  • npm run build -w @auth0/auth0-fastify — clean (incl. DTS)
  • npm run lint -w @auth0/auth0-fastify — clean
  • Full monorepo npm run build / npm run test — green, no auth0-fastify-api regressions
  • Unit coverage: static/dynamic/allow-list resolution, empty/invalid array, missing host, secure-cookie matrix
  • Integration coverage: trustProxy inference, allow-list hit/miss (500), production secure-cookie rejection

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Support appBaseUrl as a static URL or an allow-list of origins, with per-request dynamic inference when omitted.
    • Validate inferred origins against the allow-list and resolve session cookie secure behavior accordingly.
  • Bug Fixes
    • Return a server error when appBaseUrl/cookie configuration is invalid or insecure for the current environment.
  • Documentation
    • Add detailed “Dynamic Application Base URL” guidance and update resolver-mode and dynamic example README/env.
  • Tests
    • Expand unit and integration coverage for normalization, resolution, allow-list enforcement, redirects, and production cookie rejection.
  • Chores
    • Add CI workflow for building and testing examples.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 47466d2f-cb99-4b2d-8d92-55b242c0f1b7

📥 Commits

Reviewing files that changed from the base of the PR and between e877ebf and 15d04e5.

📒 Files selected for processing (4)
  • .github/workflows/examples.yml
  • examples/example-fastify-web-dynamic/src/index.spec.ts
  • examples/example-fastify-web-dynamic/src/index.ts
  • examples/example-fastify-web-dynamic/views/layout.ejs
🚧 Files skipped from review as they are similar to previous changes (4)
  • .github/workflows/examples.yml
  • examples/example-fastify-web-dynamic/src/index.spec.ts
  • examples/example-fastify-web-dynamic/views/layout.ejs
  • examples/example-fastify-web-dynamic/src/index.ts

📝 Walkthrough

Walkthrough

Adds InvalidConfigurationError, an AppBaseUrlConfig discriminated union type, and three exported helpers (normalizeAppBaseUrl, resolveAppBaseUrl, resolveSecureCookie) to normalize and per-request resolve appBaseUrl as static, allow-list, or dynamically inferred. The plugin's Auth0FastifyOptions type and all route handlers are updated to use these utilities, converting errors to HTTP 500. Documentation and tests are added throughout. A complete example application demonstrates the feature with EJS views, MSW-mocked Auth0 endpoints, and integration tests validating allow-list origin matching and host-based redirect_uri resolution.

Changes

Dynamic appBaseUrl Support in Auth0 Fastify Plugin

Layer / File(s) Summary
InvalidConfigurationError class and AppBaseUrlConfig type
packages/auth0-fastify/src/errors/index.ts, packages/auth0-fastify/src/app-base-url.ts
Adds InvalidConfigurationError extending Error with a fixed code string. Defines AppBaseUrlConfig discriminated union (static | allowlist | dynamic) and internal helper for absolute-URL validation.
Base URL normalization and resolution utilities with unit tests
packages/auth0-fastify/src/app-base-url.ts, packages/auth0-fastify/src/app-base-url.spec.ts
Implements normalizeAppBaseUrl (converts plugin option to normalized config), resolveAppBaseUrl (per-request host/protocol inference with allow-list enforcement), resolveSecureCookie (production secure-flag policy), and comprehensive test suite covering static, dynamic, allow-list modes, origin matching, and error cases.
Plugin option typing, initialization, and route handler wiring
packages/auth0-fastify/src/index.ts, packages/auth0-fastify/src/index.spec.ts
Updates Auth0FastifyOptions so appBaseUrl is optional string | string[], re-exports InvalidConfigurationError, wires normalization and secure-cookie resolution into plugin setup, introduces resolveOr500 wrapper, and applies it to all eight route handlers. Integration tests cover allow-list matching/rejection, static domain inference, logout redirect_uri resolution, and production insecure-cookie rejection.
Dynamic Application Base URL documentation
packages/auth0-fastify/EXAMPLES.md
Adds a new "Dynamic Application Base URL" section describing all appBaseUrl modes, proxy header trust requirements, and secure cookie behavior in dynamic/allow-list configurations, and updates the Resolver Mode section to cross-reference it.

Example Application: Dynamic Base URL with Allow-List

Layer / File(s) Summary
Project configuration and environment documentation
examples/example-fastify-web-dynamic/package.json, examples/example-fastify-web-dynamic/tsconfig.json, examples/example-fastify-web-dynamic/.env.example, examples/example-fastify-web-dynamic/README.md
Configures ESM module settings, npm scripts (start via tsx, build via tsc, test via Vitest), environment variable placeholders for AUTH0 config and APP_BASE_URL modes. Comprehensive guide covering dynamic base URL behavior, proxy trust, header sanitization, APP_BASE_URL configuration modes (DYNAMIC, STATIC, ALLOW-LIST), available routes, and local testing with forwarded headers.
Fastify server, Auth0 plugin registration, and route handlers
examples/example-fastify-web-dynamic/src/index.ts
Creates Fastify instance with trustProxy enabled, parses APP_BASE_URL into the three supported modes, registers Auth0 Fastify plugin with normalized config, implements public (/, /public) GET handlers rendering templates with user state and inferred origin, protects /private route with session pre-handler redirecting to login.
EJS view templates
examples/example-fastify-web-dynamic/views/layout.ejs, examples/example-fastify-web-dynamic/views/index.ejs, examples/example-fastify-web-dynamic/views/public.ejs, examples/example-fastify-web-dynamic/views/private.ejs
Provides reusable Bootstrap layout with conditional login/logout navigation based on isLoggedIn and optional user name, index page with personalized greeting and origin display with explanatory text, and simple public/private pages showing resolved request origin.
Integration tests with MSW and CI workflow
examples/example-fastify-web-dynamic/src/index.spec.ts, .github/workflows/examples.yml
Sets up environment variables before app import, mocks OIDC discovery endpoint via MSW, validates /auth/login redirects with correct redirect_uri when forwarded host matches allow-list and returns HTTP 500 when it does not. GitHub Actions workflow builds and runs CI tests for the dynamic example.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant FastifyRoute
  participant resolveOr500
  participant resolveAppBaseUrl
  participant Auth0

  Client->>FastifyRoute: GET /auth/login
  FastifyRoute->>resolveOr500: resolve(appBaseUrlConfig, request)
  resolveOr500->>resolveAppBaseUrl: resolveAppBaseUrl(config, request)

  alt static mode
    resolveAppBaseUrl-->>resolveOr500: configured URL
  else dynamic mode
    resolveAppBaseUrl->>resolveAppBaseUrl: infer from request.protocol + request.host
    resolveAppBaseUrl-->>resolveOr500: inferred URL
  else allowlist mode
    resolveAppBaseUrl->>resolveAppBaseUrl: infer origin, check against Set
    alt origin matched
      resolveAppBaseUrl-->>resolveOr500: inferred URL
    else origin not matched
      resolveAppBaseUrl-->>resolveOr500: throws InvalidConfigurationError
      resolveOr500-->>Client: HTTP 500
    end
  end

  resolveOr500-->>FastifyRoute: resolved appBaseUrl
  FastifyRoute->>Auth0: redirect with redirect_uri built from appBaseUrl
  Auth0-->>Client: auth redirect response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Poem

🐇 Hoppin' through the base URLs, one, two, three,
Static or dynamic — it's all up to me!
An allow-list guards which origins may pass,
A 500 awaits if you're out of my class.
In production I insist your cookie stays secure,
With an example to show how it all works for sure! 🌐✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main feature: dynamic application base URL support for the auth0-fastify package, which is the primary purpose of this comprehensive PR.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-dynamic-app-base-url

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
examples/example-fastify-web-dynamic/src/index.spec.ts (1)

18-23: ⚡ Quick win

Restore mutated environment variables in teardown for test isolation.

This suite mutates global process.env and leaves values behind, which can make later tests order-dependent.

Also applies to: 48-51

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/example-fastify-web-dynamic/src/index.spec.ts` around lines 18 - 23,
The test file is mutating process.env variables without restoring them after
tests complete, causing test isolation issues. Save the original values of
AUTH0_DOMAIN, AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET, AUTH0_SESSION_SECRET, and
APP_BASE_URL before setting them in the test setup, then restore these original
values in an afterEach or afterAll hook. Apply this same cleanup pattern to all
locations where environment variables are mutated (including the section
mentioned at lines 48-51).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/examples.yml:
- Around line 24-26: In the Checkout code step that uses actions/checkout, add a
with parameter to set persist-credentials to false. This prevents the GitHub
token from being persisted in the local git config, reducing credential exposure
risk in the workflow. Add the persist-credentials configuration as a new line
under the uses declaration of the checkout action.

In `@examples/example-fastify-web-dynamic/src/index.ts`:
- Around line 27-33: The fastify.register call for fastifyView has a relative
path root: './views' that depends on the process working directory, which can
cause template rendering failures if the app starts from a different directory.
Fix this by replacing the relative path with an absolute path using the path
module and __dirname, similar to the pattern already used in `@fastify/static` on
the same file. Change the root property in the fastifyView registration options
to use path.join(__dirname, 'views') instead of the relative path string.

In `@examples/example-fastify-web-dynamic/views/layout.ejs`:
- Around line 44-46: The logout link in the navigation uses unescaped EJS output
syntax (<%- ... %>) when rendering the user name from locals.user.name. Change
the unescaped output tag <%- to the escaped output tag <%= on the line
containing the logout link to ensure the user name is HTML-escaped, following
defense-in-depth security practices. This applies to both instances where
locals.user.name is displayed in the parentheses of the logout text.

---

Nitpick comments:
In `@examples/example-fastify-web-dynamic/src/index.spec.ts`:
- Around line 18-23: The test file is mutating process.env variables without
restoring them after tests complete, causing test isolation issues. Save the
original values of AUTH0_DOMAIN, AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET,
AUTH0_SESSION_SECRET, and APP_BASE_URL before setting them in the test setup,
then restore these original values in an afterEach or afterAll hook. Apply this
same cleanup pattern to all locations where environment variables are mutated
(including the section mentioned at lines 48-51).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 14ed1c08-1b03-44cc-8642-dcbde7601bfb

📥 Commits

Reviewing files that changed from the base of the PR and between 14e3854 and e877ebf.

⛔ Files ignored due to path filters (1)
  • examples/example-fastify-web-dynamic/public/img/auth0.png is excluded by !**/*.png
📒 Files selected for processing (11)
  • .github/workflows/examples.yml
  • examples/example-fastify-web-dynamic/.env.example
  • examples/example-fastify-web-dynamic/README.md
  • examples/example-fastify-web-dynamic/package.json
  • examples/example-fastify-web-dynamic/src/index.spec.ts
  • examples/example-fastify-web-dynamic/src/index.ts
  • examples/example-fastify-web-dynamic/tsconfig.json
  • examples/example-fastify-web-dynamic/views/index.ejs
  • examples/example-fastify-web-dynamic/views/layout.ejs
  • examples/example-fastify-web-dynamic/views/private.ejs
  • examples/example-fastify-web-dynamic/views/public.ejs
✅ Files skipped from review due to trivial changes (3)
  • examples/example-fastify-web-dynamic/views/public.ejs
  • examples/example-fastify-web-dynamic/views/private.ejs
  • examples/example-fastify-web-dynamic/README.md

Comment thread .github/workflows/examples.yml
Comment thread examples/example-fastify-web-dynamic/src/index.ts
Comment thread examples/example-fastify-web-dynamic/views/layout.ejs
- examples.yml: set persist-credentials: false on checkout
- index.ts: use absolute @fastify/view root (cwd-independent)
- layout.ejs: HTML-escape user.name output (<%= instead of <%-)
- index.spec.ts: restore mutated process.env in teardown
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant