Skip to content

Enterprise connect - #2799

Open
Piyush-85 wants to merge 7 commits into
mainfrom
enterprise-connect
Open

Enterprise connect#2799
Piyush-85 wants to merge 7 commits into
mainfrom
enterprise-connect

Conversation

@Piyush-85

@Piyush-85 Piyush-85 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor
  • All new/changed/fixed functionality is covered by tests (or N/A)
  • I have added documentation for all new/changed functionality (or N/A)

📋 Changes

Adds Enterprise Connect support via appType: 'b2b_integration' on Auth0Client.

New export - isFederatedDomain(auth0Domain, emailDomain):
Calls Auth0's WebFinger endpoint to determine whether an email domain is managed for enterprise SSO on the tenant. Returns true/false with a TTL cache (60 s positive, 15 s negative).
Export path: @auth0/nextjs-auth0/server. Server-side only.

New option — Auth0ClientOptions.appType: 'b2b_integration':

  • Auth0 completes the SSO handshake but writes no session cookie. onCallback is responsible for persisting identity to the app's own store.
  • onCallback return type extended to Promise<NextResponse | null | void>. Returning null/void suppresses the Auth0 session cookie and redirects to returnTo. Returning a NextResponse gives the developer full control — required for cookie-based sessions.
  • Session-dependent methods (getSession, getAccessToken, buildSessionTransferRedirect, getTokenByBackchannelAuth, mfa, passwordless, passkey, and others) throw InvalidConfigurationError with actionable guidance rather than returning null silently. Synchronous methods throw synchronously; async methods
    return a rejected promise.
  • Initialization warns if offline_access is in scope (no refresh tokens issued for this app type) or organization is not being passed per login.

EC-aware logout:
The existing logout path set federated= as an empty string in EC mode because no id_token_hint is available without an Auth0 session. This meant the enterprise IdP session was never terminated, causing the next login to silently reuse the previous user's session. handleLogout now detects EC mode and uses the OIDC end_session_endpoint directly with federated=true.

Documentation:

  • EXAMPLES.md — new Enterprise Connect section covering isFederatedDomain, SDK initialization, login form, protected pages, logout, and the unavailable
    methods list.
  • examples/with-enterprise-connect — new example app with a working end-to-end EC flow including domain discovery, cookie-based own-session pattern, and federated logout.

📎 References

RFC 7033 — WebFinger (IETF standard)

🎯 Testing

  • 23 unit tests for isFederatedDomain covering all WebFinger response codes, TTL cache, case normalization, and rate limiting.
  • 9 tests for the stateless passthrough callback path.
  • 13 tests for EC mode enforcement (blocked methods, init warnings, available methods).
  • 5 tests for EC-aware logout (federated=true string value, no id_token_hint, fallback to /oidc/logout when end_session_endpoint absent).
  • 2 tests for the onCallback void warning.
  • All 1913 tests pass. npx tsc --noEmit clean. npm run lint clean.
  • End-to-end tested against a live tenant with a SAML enterprise connection. Full login → dashboard → logout → re-login cycle confirmed working with correct federated logout.

Summary by CodeRabbit

  • New Features

    • Added Enterprise Connect support for B2B integrations with stateless authentication and application-managed sessions.
    • Added federated domain discovery and OIDC-compatible federated logout.
    • Added configuration options and callback handling for Enterprise Connect applications.
    • Added safeguards for session-dependent features unavailable in stateless mode.
  • Documentation

    • Added setup and usage guidance for login, logout, protected pages, and supported capabilities.
  • Bug Fixes

    • Added clearer warnings and validation for unsupported configurations and callback behavior.

@Piyush-85
Piyush-85 requested a review from a team as a code owner August 9, 2026 15:13
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

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: 0fcfce6c-0044-4072-9589-913446720fe7

📥 Commits

Reviewing files that changed from the base of the PR and between 1d402be and 69a5eeb.

📒 Files selected for processing (2)
  • EXAMPLES.md
  • src/server/auth-client.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/server/auth-client.test.ts
  • EXAMPLES.md

📝 Walkthrough

Walkthrough

Adds Enterprise Connect support for B2B integrations. The change provides WebFinger-based domain discovery, disables incompatible Auth0-managed session features, supports stateless callbacks and federated logout, exports the discovery utility, and adds documentation and tests.

Changes

Enterprise Connect

Layer / File(s) Summary
Federated domain discovery
src/utils/webfingerCache.ts, src/utils/webfingerCache.test.ts, src/server/index.ts, EXAMPLES.md
Adds cached WebFinger discovery with positive and negative TTLs, bounded FIFO storage, response validation, tenant-specific cache keys, tests, and a server-side export. The examples document email-domain routing.
Enterprise Connect client surface
src/server/client.ts, src/server/client.test.ts, EXAMPLES.md
Adds appType: "b2b_integration", replaces session-backed members with configuration errors, preserves supported members, forwards the option to AuthClient, and warns about incompatible configuration.
Stateless callback and logout
src/server/auth-client.ts, src/server/auth-client.test.ts, EXAMPLES.md
Allows successful callbacks to pass through without Auth0 session persistence, validates required responses for error paths, clears transaction state, and redirects Enterprise Connect logout through the OIDC end-session endpoint. The examples show application-owned sessions and logout handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • auth0/nextjs-auth0#2751: Both changes modify src/server/auth-client.ts logout handling. This change adds Enterprise Connect logout behavior, while the related PR adds refresh-token revocation logic.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title identifies the main change, which is adding Enterprise Connect support.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch enterprise-connect

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.

@codecov-commenter

codecov-commenter commented Aug 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.89796% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.09%. Comparing base (badf948) to head (69a5eeb).

Files with missing lines Patch % Lines
src/server/auth-client.ts 85.45% 8 Missing ⚠️
src/server/index.ts 0.00% 1 Missing ⚠️
src/utils/webfingerCache.ts 98.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2799      +/-   ##
==========================================
+ Coverage   87.98%   88.09%   +0.10%     
==========================================
  Files          80       81       +1     
  Lines       11514    11709     +195     
  Branches     2385     2434      +49     
==========================================
+ Hits        10131    10315     +184     
- Misses       1338     1349      +11     
  Partials       45       45              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/auth-client.ts (1)

1474-1479: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

The "no Auth0 session cookie" invariant is not enforced across the whole request lifecycle. appType: 'b2b_integration' is documented as never writing an Auth0 session cookie (src/server/client.ts lines 307-308, EXAMPLES.md Line 6133), but the mode check is applied only on the standard redirect callback path. One path can still create the cookie, and the logout path cannot remove it. The combined result is a session cookie that the application believes cannot exist, cannot read through getSession (which throws in this mode), and cannot clear through logout.

  • src/server/auth-client.ts#L1474-L1479: gate the popup branch on appType. It ignores the onCallback return value and always calls sessionStore.set (Line 1518, Line 1588), and handleLogin accepts challengeMode=popup from the query string with no mode check. Reject challengeMode: 'popup' when appType === "b2b_integration", since popup step-up depends on an Auth0-managed session.
  • src/server/auth-client.ts#L1053-L1069: call sessionStore.delete(req.cookies, ecLogoutResponse.cookies) before returning, guarded by !hasDomainMismatch, and call addCacheControlHeadersForSession. This branch returns before the cleanup at lines 1165-1168, so a cookie written by the popup path or carried over from a deployment that adopted appType later survives logout.
🤖 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 `@src/server/auth-client.ts` around lines 1474 - 1479, The popup authentication
branch around getSessionWithDomainCheck must reject challengeMode "popup" when
appType is "b2b_integration", preventing sessionStore.set from creating an Auth0
session cookie; preserve existing popup behavior for other app types. In
src/server/auth-client.ts lines 1053-1069, before the early logout return, call
sessionStore.delete(req.cookies, ecLogoutResponse.cookies) only when
!hasDomainMismatch, then call addCacheControlHeadersForSession so stale session
cookies are removed and the response receives session cache headers.
🧹 Nitpick comments (8)
src/server/auth-client.ts (2)

232-246: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the return-value documentation above the type alias.

The JSDoc block sits inside the parameter list, after session. It parses, but TSDoc attaches it to nothing. Editors show no hover text for OnCallbackHook, and a reader sees a return-value description positioned as if it documented the session parameter. This is the main contract users need for Enterprise Connect, so it should be visible on hover.

📝 Proposed fix
+/**
+ * Returning `null` or nothing suppresses the Auth0 session cookie — the hook is
+ * expected to have persisted identity elsewhere. This is only valid on the
+ * successful callback path; error paths and the connected-account flow still
+ * require a `NextResponse`.
+ *
+ * When `appType: "b2b_integration"` is set the cookie is suppressed regardless
+ * of the return value, so returning nothing is the idiomatic Enterprise Connect
+ * pattern. Return a `NextResponse` to control the redirect destination.
+ */
 export type OnCallbackHook = (
   error: SdkError | null,
   ctx: OnCallbackContext,
   session: SessionData | null
-  /**
-   * Returning `null` or nothing suppresses the Auth0 session cookie — the hook is
-   * expected to have persisted identity elsewhere. This is only valid on the
-   * successful callback path; error paths and the connected-account flow still
-   * require a `NextResponse`.
-   *
-   * When `appType: "b2b_integration"` is set the cookie is suppressed regardless
-   * of the return value, so returning nothing is the idiomatic Enterprise Connect
-   * pattern. Return a `NextResponse` to control the redirect destination.
-   */
 ) => Promise<NextResponse | null | void>;
🤖 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 `@src/server/auth-client.ts` around lines 232 - 246, Move the existing
return-value JSDoc block so it immediately precedes the OnCallbackHook type
alias, ensuring it documents the alias and appears in editor hover text. Keep
the callback parameters and return type unchanged.

2890-2899: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the duplicated error message.

This message is identical to the one at lines 1198-1202. A shared constant keeps the two guards in sync if the wording changes.

This is optional.

🤖 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 `@src/server/auth-client.ts` around lines 2890 - 2899, Extract the duplicated
InvalidConfigurationError message into a shared constant and reuse it in both
the guard near onCallback and the matching guard around the earlier callback
path. Preserve the existing wording and error behavior while ensuring future
message changes stay synchronized.
src/server/client.test.ts (2)

2049-2074: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider asserting the getters still work without appType.

This block confirms getSession and the warnings are unaffected. It does not confirm that mfa, passkey, and passwordless still return their sub-clients. Those three receive the most invasive replacement — a throwing get descriptor — so a negative assertion is cheap protection against the guard leaking outside Enterprise Connect mode.

it.each(["mfa", "passkey", "passwordless"])(
  "leaves %s accessible",
  (member) => {
    const client = new Auth0Client({}) as unknown as Record<string, unknown>;
    expect(() => client[member]).not.toThrow();
    expect(client[member]).toBeDefined();
  }
);
🤖 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 `@src/server/client.test.ts` around lines 2049 - 2074, Extend the “without
appType (no regression)” tests with a parameterized case for the Auth0Client
getters mfa, passkey, and passwordless. Instantiate Auth0Client without appType,
assert each corresponding property access does not throw, and verify it returns
a defined sub-client.

1757-1768: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider restoring the environment variables in afterEach.

beforeEach sets five process.env entries. afterEach restores only the console.warn spy. The values persist into later suites in this file and into other suites in the same worker.

This is optional if the file already resets environment state globally.

🤖 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 `@src/server/client.test.ts` around lines 1757 - 1768, Restore the five
process.env entries assigned in the beforeEach hook after each test, alongside
consoleWarnSpy.mockRestore in afterEach. Preserve any prior or unset values
rather than deleting or overwriting them unconditionally, and avoid changes if
an existing file-wide environment reset already handles these keys.
src/utils/webfingerCache.test.ts (1)

168-264: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a test for CACHE_MAX_SIZE eviction.

The caching suite covers TTL, bypass, and key isolation. It does not cover the FIFO eviction branch in setCacheEntry. A test that fills the cache past 1,000 distinct domains and asserts the oldest entry re-fetches would lock in the bound.

This is optional. The eviction path is small and currently correct.

🤖 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 `@src/utils/webfingerCache.test.ts` around lines 168 - 264, Add an optional
caching test in the “caching” suite that calls isFederatedDomain for more than
CACHE_MAX_SIZE distinct domains, then repeats the oldest domain and verifies
fetchSpy was called again because FIFO eviction removed its entry. Keep the test
focused on the cache bound and use distinct domain keys.
src/utils/webfingerCache.ts (1)

95-135: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider coalescing concurrent lookups for the same key.

The cache stores only settled results. Concurrent logins for the same cold domain each issue their own WebFinger request. That is the exact burst pattern that produces the 429 handled on Line 116. Storing the in-flight Promise in the map, keyed the same way, collapses the burst into one request.

This is optional. The current behavior is correct and degrades safely.

🤖 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 `@src/utils/webfingerCache.ts` around lines 95 - 135, Optionally update the
WebFinger lookup flow around the cache key and request function to coalesce
concurrent cold-cache lookups: store the in-flight Promise in the same keyed
map, return it for subsequent requests, and replace it with the settled result
using the existing TTL behavior. Preserve current handling for successful, 404,
429, and error responses, including safe false returns and no caching where
currently specified.
src/server/auth-client.test.ts (1)

6029-6079: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse runCallback for the remaining tests.

These three tests rebuild the same AuthClient, URL, transaction state, and encrypted cookie that runCallback (lines 5872-5927) already builds. They differ only by beforeSessionSaved and a console.warn spy. That is roughly 150 duplicated lines directly below the helper introduced to prevent them.

Add optional beforeSessionSaved to the helper options and use it in all three.

♻️ Proposed helper extension
       async function runCallback({
         onCallback,
         appType,
+        beforeSessionSaved,
         returnTo = "/dashboard"
       }: {
         onCallback: AuthClientOptions["onCallback"];
         appType?: "b2b_integration";
+        beforeSessionSaved?: AuthClientOptions["beforeSessionSaved"];
         returnTo?: string;
       }) {
           onCallback,
+          beforeSessionSaved,
           appType
         });
🤖 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 `@src/server/auth-client.test.ts` around lines 6029 - 6079, Extend the existing
runCallback helper with an optional beforeSessionSaved callback option and pass
it into the AuthClient configuration. Replace the duplicated AuthClient,
callback URL, transaction state, and encrypted-cookie setup in all three
remaining tests, including “does not run beforeSessionSaved on the passthrough
path,” with runCallback calls while preserving each test’s callback and
console.warn behavior.
src/server/client.ts (1)

109-165: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Type the registry keys against Auth0Client to catch renames.

EC_UNAVAILABLE_MEMBERS is Record<string, string>, and the two companion sets hold plain strings. Nothing links them to the real members. Three silent failure modes follow:

  • A member is renamed. The entry keeps the old key and the member becomes available again in Enterprise Connect mode.
  • A new getter is added to EC_UNAVAILABLE_MEMBERS but omitted from EC_UNAVAILABLE_GETTERS. disableSessionMembersForEnterpriseConnect installs a value property instead of a get, so client.newGetter returns a throwing function rather than throwing on access.
  • buildSessionTransferRedirect becomes async and stays in EC_UNAVAILABLE_SYNC_METHODS, producing the unhandled-rejection behavior the comment on lines 161-163 warns about.

The doc block at lines 92-107 asks maintainers to keep these in sync manually. A key type makes the compiler do it.

♻️ Proposed typing
-const EC_UNAVAILABLE_MEMBERS: Record<string, string> = {
+type EcUnavailableMember = keyof Auth0Client;
+
+const EC_UNAVAILABLE_MEMBERS: Partial<Record<EcUnavailableMember, string>> = {
-const EC_UNAVAILABLE_GETTERS = new Set(["passwordless", "passkey", "mfa"]);
+const EC_UNAVAILABLE_GETTERS = new Set<EcUnavailableMember>([
+  "passwordless",
+  "passkey",
+  "mfa"
+]);
-const EC_UNAVAILABLE_SYNC_METHODS = new Set(["buildSessionTransferRedirect"]);
+const EC_UNAVAILABLE_SYNC_METHODS = new Set<EcUnavailableMember>([
+  "buildSessionTransferRedirect"
+]);
🤖 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 `@src/server/client.ts` around lines 109 - 165, Type EC_UNAVAILABLE_MEMBERS
keys against the corresponding members of Auth0Client instead of Record<string,
string>, and type EC_UNAVAILABLE_GETTERS and EC_UNAVAILABLE_SYNC_METHODS using
those same derived member-key unions. Ensure the registry and companion sets are
compiler-checked when Auth0Client members are renamed, added, or change between
getter and method/async behavior, preserving the existing runtime handling in
disableSessionMembersForEnterpriseConnect.
🤖 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 `@EXAMPLES.md`:
- Line 6150: Update the isFederatedDomain example guidance to explicitly
document that proxying the check through an unauthenticated browser-accessible
route enables domain enumeration. Instruct implementers to rate-limit the route
and treat its response as public information, while retaining the
server-side-only requirement.
- Line 6271: Update the Enterprise Connect reference in EXAMPLES.md to point to
the matching existing example directory, or remove the reference if no
corresponding example exists; ensure no link remains to
examples/with-enterprise-connect.
- Around line 6168-6187: Update the callback example around the session creation
flow: validate session.user["org_id"] against the organization expected for this
login before trusting it; replace the unsigned base64 app_session value with the
existing signed or encrypted session mechanism; set secure: true on the cookie;
and change the handleCallback error path to return a NextResponse redirect to
the error page instead of throwing, preserving the onCallback response contract.
- Around line 6142-6147: Update the POST route to validate that email is present
and contains a domain after “@” before calling isFederatedDomain. Retrieve the
database-backed connection and orgId values for the email domain, and return
isFederated, connection, and orgId from the route so the login form’s
destructuring and /auth/login query parameters receive defined values.

In `@src/server/auth-client.test.ts`:
- Line 6084: Move restoration of the console.warn spy from the individual tests
into an afterEach hook in the relevant test scope, using warnSpy.mockRestore()
or vi.restoreAllMocks(). Remove the test-local restore statements while
preserving the existing spy setup and assertions.
- Around line 4155-4180: Update the test “does not include id_token_hint even
when a session exists” to create and persist a session in the existing
StatelessSessionStore, then attach the resulting session cookie to the logout
request. Keep the existing assertion and configuration unchanged so handleLogout
exercises the session-present Enterprise Connect path and verifies that
id_token_hint remains omitted.
- Around line 4245-4268: Update the “clears transaction cookies on EC logout”
test to seed a transaction cookie on the logout request, using the configured
transaction store and its expected cookie name/value format. After handleLogout
returns, assert the response clears that cookie with maxAge 0, while retaining
the existing redirect-status assertion; this should specifically exercise
AuthClient.handleLogout’s transactionStore.deleteAll path.

In `@src/server/auth-client.ts`:
- Around line 1645-1656: Update the passthrough handling around the callback
result condition so any falsy res emits a diagnostic warning, including
non-Enterprise-Connect applications. Preserve the existing Enterprise
Connect-specific warning text for appType "b2b_integration", and add an
appropriate warning for other app types before the redirect/session-creation
path continues.

In `@src/server/client.test.ts`:
- Around line 1926-1937: Remove the duplicate test around
client.buildSessionTransferRedirect, or update it to explicitly verify that the
method does not return a promise and cannot leave an unhandled rejection; retain
the existing stricter throwing test separately.

In `@src/server/client.ts`:
- Around line 314-327: The Auth0Client JSDoc example currently demonstrates a
static organization that conflicts with the documented multi-organization
guidance. Update the example’s authorizationParameters to remove organization,
and add a concise comment showing that organization should be supplied per login
while noting the static value is appropriate only for a client serving exactly
one organization.

In `@src/utils/webfingerCache.ts`:
- Around line 88-93: Update the WebFinger fetch in isFederatedDomain to pass an
AbortSignal.timeout using the SDK’s configured HTTP timeout, matching
AuthClient.httpOptions. Preserve the existing catch behavior so a timeout
AbortError returns false through the current fallback path.

---

Outside diff comments:
In `@src/server/auth-client.ts`:
- Around line 1474-1479: The popup authentication branch around
getSessionWithDomainCheck must reject challengeMode "popup" when appType is
"b2b_integration", preventing sessionStore.set from creating an Auth0 session
cookie; preserve existing popup behavior for other app types. In
src/server/auth-client.ts lines 1053-1069, before the early logout return, call
sessionStore.delete(req.cookies, ecLogoutResponse.cookies) only when
!hasDomainMismatch, then call addCacheControlHeadersForSession so stale session
cookies are removed and the response receives session cache headers.

---

Nitpick comments:
In `@src/server/auth-client.test.ts`:
- Around line 6029-6079: Extend the existing runCallback helper with an optional
beforeSessionSaved callback option and pass it into the AuthClient
configuration. Replace the duplicated AuthClient, callback URL, transaction
state, and encrypted-cookie setup in all three remaining tests, including “does
not run beforeSessionSaved on the passthrough path,” with runCallback calls
while preserving each test’s callback and console.warn behavior.

In `@src/server/auth-client.ts`:
- Around line 232-246: Move the existing return-value JSDoc block so it
immediately precedes the OnCallbackHook type alias, ensuring it documents the
alias and appears in editor hover text. Keep the callback parameters and return
type unchanged.
- Around line 2890-2899: Extract the duplicated InvalidConfigurationError
message into a shared constant and reuse it in both the guard near onCallback
and the matching guard around the earlier callback path. Preserve the existing
wording and error behavior while ensuring future message changes stay
synchronized.

In `@src/server/client.test.ts`:
- Around line 2049-2074: Extend the “without appType (no regression)” tests with
a parameterized case for the Auth0Client getters mfa, passkey, and passwordless.
Instantiate Auth0Client without appType, assert each corresponding property
access does not throw, and verify it returns a defined sub-client.
- Around line 1757-1768: Restore the five process.env entries assigned in the
beforeEach hook after each test, alongside consoleWarnSpy.mockRestore in
afterEach. Preserve any prior or unset values rather than deleting or
overwriting them unconditionally, and avoid changes if an existing file-wide
environment reset already handles these keys.

In `@src/server/client.ts`:
- Around line 109-165: Type EC_UNAVAILABLE_MEMBERS keys against the
corresponding members of Auth0Client instead of Record<string, string>, and type
EC_UNAVAILABLE_GETTERS and EC_UNAVAILABLE_SYNC_METHODS using those same derived
member-key unions. Ensure the registry and companion sets are compiler-checked
when Auth0Client members are renamed, added, or change between getter and
method/async behavior, preserving the existing runtime handling in
disableSessionMembersForEnterpriseConnect.

In `@src/utils/webfingerCache.test.ts`:
- Around line 168-264: Add an optional caching test in the “caching” suite that
calls isFederatedDomain for more than CACHE_MAX_SIZE distinct domains, then
repeats the oldest domain and verifies fetchSpy was called again because FIFO
eviction removed its entry. Keep the test focused on the cache bound and use
distinct domain keys.

In `@src/utils/webfingerCache.ts`:
- Around line 95-135: Optionally update the WebFinger lookup flow around the
cache key and request function to coalesce concurrent cold-cache lookups: store
the in-flight Promise in the same keyed map, return it for subsequent requests,
and replace it with the settled result using the existing TTL behavior. Preserve
current handling for successful, 404, 429, and error responses, including safe
false returns and no caching where currently specified.
🪄 Autofix

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: 8767f359-8fd5-486c-b445-82cdc784ee42

📥 Commits

Reviewing files that changed from the base of the PR and between badf948 and 20cf452.

📒 Files selected for processing (8)
  • EXAMPLES.md
  • src/server/auth-client.test.ts
  • src/server/auth-client.ts
  • src/server/client.test.ts
  • src/server/client.ts
  • src/server/index.ts
  • src/utils/webfingerCache.test.ts
  • src/utils/webfingerCache.ts

Comment thread EXAMPLES.md
Comment on lines +6142 to +6147
export async function POST(req: NextRequest) {
const { email } = await req.json();
const emailDomain = email.split("@")[1];
const isFederated = await isFederatedDomain(process.env.AUTH0_DOMAIN!, emailDomain);
return NextResponse.json({ isFederated });
}

@coderabbitai coderabbitai Bot Aug 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

The example route does not return the fields the login form reads.

/api/check-domain returns { isFederated } only. The login form at Line 6204 destructures { isFederated, connection, orgId } from that same response, and Line 6212 puts both into the /auth/login query string. A reader who copies both snippets gets connection=undefined&organization=undefined.

The comment on Line 6211 states that connection and orgId come from the database through this route, so the route must return them.

Also validate email before splitting. If the body has no @, email.split("@")[1] is undefined and the WebFinger resource becomes urn:auth0:discovery:domain:undefined.

📝 Proposed fix for the route contract
 export async function POST(req: NextRequest) {
   const { email } = await req.json();
-  const emailDomain = email.split("@")[1];
-  const isFederated = await isFederatedDomain(process.env.AUTH0_DOMAIN!, emailDomain);
-  return NextResponse.json({ isFederated });
+  const emailDomain =
+    typeof email === "string" ? email.split("@")[1]?.toLowerCase() : undefined;
+
+  if (!emailDomain) {
+    return NextResponse.json({ isFederated: false }, { status: 400 });
+  }
+
+  const isFederated = await isFederatedDomain(process.env.AUTH0_DOMAIN!, emailDomain);
+
+  if (!isFederated) {
+    return NextResponse.json({ isFederated: false });
+  }
+
+  // Look up the enterprise connection and organization for this domain.
+  const tenant = await db.tenants.findByEmailDomain(emailDomain);
+
+  return NextResponse.json({
+    isFederated: Boolean(tenant),
+    connection: tenant?.connection,
+    orgId: tenant?.orgId
+  });
 }
🤖 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.md` around lines 6142 - 6147, Update the POST route to validate that
email is present and contains a domain after “@” before calling
isFederatedDomain. Retrieve the database-backed connection and orgId values for
the email domain, and return isFederated, connection, and orgId from the route
so the login form’s destructuring and /auth/login query parameters receive
defined values.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this should be fixed, check it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline EXAMPLES.md --items all --type function --match 'POST|LoginPage'

printf '\n--- Domain-discovery example ---\n'
sed -n '6135,6185p' EXAMPLES.md

printf '\n--- Login-form example ---\n'
sed -n '6185,6235p' EXAMPLES.md

Length of output: 4279


@Piyush-85, I checked the current PR branch.

The route now returns connection and orgId for federated domains. This fixes the login form response-contract issue.

The email validation is still missing. email.split("@")[1] can throw when email is not a string. It can also produce undefined when the email has no domain. The route should validate the input before it calls isFederatedDomain.

The review comment remains valid for the input-validation part.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should be fixed, check

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Enterprise Connect domain-discovery example ---'
sed -n '6135,6185p' EXAMPLES.md

printf '%s\n' '--- Enterprise Connect login-form example ---'
sed -n '6212,6250p' EXAMPLES.md

Length of output: 3808


@Piyush-85, I checked the current branch.

The route now returns connection and orgId. This fixes the login-form response contract.

The email validation is still incomplete.

  • If email is not a string, email.includes("@") throws.
  • If email is "user@", the check passes and emailDomain is empty.

The route should validate the type and the domain before it calls isFederatedDomain. The review comment remains unresolved.

You are interacting with an AI system.

Comment thread EXAMPLES.md Outdated
}
```

`isFederatedDomain` calls Auth0's WebFinger endpoint and caches results (60 s for managed domains, 15 s for unmanaged). Call it server-side only — never from the browser.

@coderabbitai coderabbitai Bot Aug 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Document the enumeration risk of proxying this check to the browser.

isFederatedDomain warns in its JSDoc that browser access "would expose the Auth0 domain and allow clients to enumerate your customers' domains" (src/utils/webfingerCache.ts, lines 50-51). The example route restores that capability: it is an unauthenticated POST that answers "is this domain one of your enterprise customers?" for any submitted domain.

"Call it server-side only" does not cover this. Add guidance to rate limit the route and to treat the answer as public information.

🤖 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.md` at line 6150, Update the isFederatedDomain example guidance to
explicitly document that proxying the check through an unauthenticated
browser-accessible route enables domain enumeration. Instruct implementers to
rate-limit the route and treat its response as public information, while
retaining the server-side-only requirement.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed, rereview

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

line=$(rg -n '^## Enterprise Connect \(B2B Integration\)$' EXAMPLES.md | cut -d: -f1)
if [ -z "$line" ]; then
  echo "Enterprise Connect section not found in EXAMPLES.md" >&2
  exit 1
fi
start=$((line))
end=$((line + 70))
sed -n "${start},${end}p" EXAMPLES.md

Length of output: 3686


@Piyush-85, the update documents the risk of calling isFederatedDomain from the browser directly.

The example still exposes the same result through an unauthenticated POST /api/check-domain route. The guidance must also state that this route enables domain enumeration, must be rate-limited, and must treat its response as public information.

The review comment remains unresolved.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Check again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

section_line="$(rg -n '^## Enterprise Connect \(B2B Integration\)$' EXAMPLES.md | cut -d: -f1)"
if [ -z "$section_line" ]; then
  echo "Enterprise Connect section not found in EXAMPLES.md" >&2
  exit 1
fi

sed -n "${section_line},$((section_line + 85))p" EXAMPLES.md

Length of output: 4077


@Piyush-85, I checked the current EXAMPLES.md update.

The direct browser-call warning is now present. The example still exposes an unauthenticated POST /api/check-domain route. The documentation does not yet instruct users to:

  • Rate-limit this route.
  • Treat its response as public information.

The route also returns connection and orgId for managed domains. This makes the guidance necessary.

The review comment remains unresolved.

You are interacting with an AI system.

Comment thread EXAMPLES.md Outdated
Comment thread EXAMPLES.md
Comment thread src/server/auth-client.test.ts Outdated
Comment thread src/server/auth-client.test.ts
Comment thread src/server/auth-client.ts
Comment thread src/server/client.test.ts Outdated
Comment thread src/server/client.ts
Comment thread src/utils/webfingerCache.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/utils/webfingerCache.ts (1)

113-137: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Cancel response bodies before exiting non-OK branches.

In the non-OK response path, isFederatedDomain returns without consuming or cancelling res.body. Under Node fetch, leaving response streams to garbage collection can prevent TCP connections from returning to the pool to other requests. Handle body cleanup early before the status-specific return branches.

🤖 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 `@src/utils/webfingerCache.ts` around lines 113 - 137, Update
isFederatedDomain’s non-OK response handling to cancel or consume res.body
before the 404 and 429 early returns and before falling through for other error
statuses. Perform this cleanup immediately after receiving the response, while
preserving the existing caching and status-specific behavior.

Source: MCP tools

🤖 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.

Outside diff comments:
In `@src/utils/webfingerCache.ts`:
- Around line 113-137: Update isFederatedDomain’s non-OK response handling to
cancel or consume res.body before the 404 and 429 early returns and before
falling through for other error statuses. Perform this cleanup immediately after
receiving the response, while preserving the existing caching and
status-specific behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f99b0aa-02d5-44c6-8a75-f7086692e41d

📥 Commits

Reviewing files that changed from the base of the PR and between 20cf452 and f60dbd6.

📒 Files selected for processing (6)
  • EXAMPLES.md
  • src/server/auth-client.test.ts
  • src/server/auth-client.ts
  • src/server/client.test.ts
  • src/server/client.ts
  • src/utils/webfingerCache.ts
💤 Files with no reviewable changes (1)
  • src/server/client.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/server/auth-client.test.ts
  • src/server/client.ts
  • EXAMPLES.md
  • src/server/auth-client.ts

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.

2 participants