Skip to content

Expose default oncallback#2667

Closed
Piyush-85 wants to merge 3 commits into
mainfrom
expose-default-oncallback
Closed

Expose default oncallback#2667
Piyush-85 wants to merge 3 commits into
mainfrom
expose-default-oncallback

Conversation

@Piyush-85

@Piyush-85 Piyush-85 commented May 16, 2026

Copy link
Copy Markdown
Contributor

Summary

The onCallback hook currently requires users to re-implement the SDK's default redirect/error behaviour from
scratch if they want any custom side effects (e.g. syncing the user to a database). This change passes the
SDK's built-in callback handler as a fourth argument (defaultOnCallback) to every onCallback invocation, so
custom hooks can run their own logic and then delegate to the default behaviour without duplicating it.

Changes:

  • Added defaultOnCallback as the fourth parameter of OnCallbackHook, typed as (error, ctx) =>
    Promise
  • Passed this.defaultOnCallback.bind(this) at all onCallback call sites (including the popup/postMessage
    flow added after this was originally authored)
  • Added full JSDoc to OnCallbackHook with @param descriptions and a usage example — surfaces on hover in VS
    Code and WebStorm

Usage

  const auth0 = new Auth0Client({                                                                           
    async onCallback(error, ctx, session, defaultOnCallback) {                                                
      if (!error && session) {                                                                                
        await db.upsertUser(session.user); // your side effect                                                
      }                                                                                                       
      return defaultOnCallback(error, ctx); // keep default redirect behaviour                                
    }                                                                                                         
  });  

Previously, users had to manually inspect ctx.returnTo and construct a NextResponse.redirect(...) themselves
to replicate what the SDK does by default.


Co-authored-by: Dan Haughey (greenlynx) — original implementation in #2488

greenlynx and others added 3 commits May 17, 2026 00:12
@Piyush-85
Piyush-85 requested a review from a team as a code owner May 16, 2026 18:51
@coderabbitai

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR extends the onCallback hook signature from three parameters to four, adding a defaultOnCallback function parameter that allows hook authors to invoke the SDK's default redirect/error behavior after running side effects. The type contract, constructor binding, all invocation sites, tests, and documentation are updated consistently.

Changes

onCallback Hook Contract and Implementation

Layer / File(s) Summary
Hook contract definition
src/server/auth-client.ts
OnCallbackHook type signature is extended to include defaultOnCallback: (error: SdkError | null, ctx: OnCallbackContext) => Promise<NextResponse> as the fourth parameter alongside error, ctx, and session.
Constructor binding of default handler
src/server/auth-client.ts
Constructor assignment for this.onCallback is updated to bind the SDK's default handler so all callback invocations pass the updated signature consistently.
Success path hook invocations
src/server/auth-client.ts
CONNECT_CODE flow, popup/postMessage branches (with and without existing session), and standard redirect success paths all invoke onCallback with the new four-parameter signature including bound defaultOnCallback.
Error path hook invocations
src/server/auth-client.ts
Invalid/missing state, popup/postMessage error, and standard redirect error paths invoke onCallback with error state and the bound defaultOnCallback handler using the new signature.
Test assertion updates
src/server/auth-client.test.ts
Authorization callback and connect-account callback test expectations are updated to include the trailing expect.any(Function) argument and explicitly assert null session for error cases, matching the new invocation signature across all scenarios.
Documentation updates
EXAMPLES.md, V4_MIGRATION_GUIDE.md
Examples and migration guide reflect the new four-parameter onCallback(error, context, session, defaultOnCallback) hook signature.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A hook that's now complete and true,
Four parameters to call on you—
With defaults bound, you're free to choose,
To keep the path or lose the fuse!
Default behavior at your hand—
A callback hook that's finely planned! 🎯

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Expose default oncallback' directly corresponds to the main objective: exposing a defaultOnCallback parameter to the public OnCallbackHook API, enabling users to invoke SDK default behavior after custom logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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 expose-default-oncallback

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

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.61538% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.74%. Comparing base (f75779c) to head (a5a60de).

Files with missing lines Patch % Lines
src/server/auth-client.ts 84.61% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2667      +/-   ##
==========================================
- Coverage   89.76%   89.74%   -0.03%     
==========================================
  Files          70       70              
  Lines        8254     8285      +31     
  Branches     1759     1760       +1     
==========================================
+ Hits         7409     7435      +26     
- Misses        833      838       +5     
  Partials       12       12              

☔ View full report in Codecov by Sentry.
📢 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: 1

🧹 Nitpick comments (2)
EXAMPLES.md (1)

2547-2567: ⚡ Quick win

Add the defaultOnCallback parameter to the example signature and demonstrate its usage.

The documentation describes onCallback as receiving 4 parameters, but the example only shows 3. Consider adding the fourth parameter to the signature and providing an example that demonstrates how to use defaultOnCallback, such as running custom logic before delegating to the default behavior.

📚 Suggested example demonstrating defaultOnCallback usage
 export const auth0 = new Auth0Client({
-  async onCallback(error, context, session) {
+  async onCallback(error, context, session, defaultOnCallback) {
+    // Example 1: Run custom logic then delegate to default behavior
+    if (!error && session) {
+      // Log successful authentication
+      console.log(`User ${session.user.sub} authenticated successfully`);
+      
+      // Delegate to the default onCallback behavior
+      return defaultOnCallback(error, context, session);
+    }
+
+    // Example 2: Custom error handling, custom redirect for success
     const appBaseUrl = context.appBaseUrl ?? process.env.APP_BASE_URL;

     // redirect the user to a custom error page
     if (error) {
       return NextResponse.redirect(
         new URL(`/error?error=${error.message}`, appBaseUrl)
       );
     }

     // complete the redirect to the provided returnTo URL
     return NextResponse.redirect(
       new URL(context.returnTo || "/", appBaseUrl)
     );
   }
 });
🤖 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 2547 - 2567, Update the example Auth0Client
onCallback signature to include the fourth parameter defaultOnCallback and show
delegating to it after custom logic: in the Auth0Client instantiation's async
onCallback(error, context, session, defaultOnCallback) example, demonstrate
checking error and doing custom behavior (e.g., logging or redirecting to a
custom error page) and then calling await defaultOnCallback(error, context,
session) to run the built-in behavior (or return its NextResponse) when
appropriate; reference Auth0Client, onCallback, defaultOnCallback, NextResponse,
context, session, and error so readers can find the handler and see how to run
the default flow after custom work.
V4_MIGRATION_GUIDE.md (1)

378-394: ⚡ Quick win

Demonstrate the new defaultOnCallback parameter or clarify backward compatibility.

The signature now includes defaultOnCallback as a fourth parameter, but the example doesn't use it. Since this is a migration guide, consider either:

  1. Showing how to use the new parameter (especially if it enables new patterns), or
  2. Adding a note that the parameter is optional and existing 3-parameter implementations will continue to work
📚 Suggested enhancement options

Option 1: Add example using defaultOnCallback

 export const auth0 = new Auth0Client({
   async onCallback(error, context, session, defaultOnCallback) {
     if (error) {
       console.error('Authentication error:', error);
       return NextResponse.redirect(
         new URL('/error', process.env.APP_BASE_URL)
       );
     }

     // Custom logic after successful authentication
     if (session) {
       console.log(`User ${session.user.sub} logged in successfully`);
     }

+    // Delegate to default behavior for redirect handling
+    return defaultOnCallback(error, context, session);
-    return NextResponse.redirect(
-      new URL(context.returnTo || "/", process.env.APP_BASE_URL)
-    );
   }
 });

Option 2: Add backward compatibility note

 export const auth0 = new Auth0Client({
   async onCallback(error, context, session, defaultOnCallback) {
+    // Note: The defaultOnCallback parameter is new in v4. It allows you to
+    // run side effects and then delegate to the SDK's default redirect behavior.
+    // If you don't need it, you can omit it from your signature (backward compatible).
+
     if (error) {
       console.error('Authentication error:', error);
       return NextResponse.redirect(
         new URL('/error', process.env.APP_BASE_URL)
       );
     }
     // ...
   }
 });
🤖 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 `@V4_MIGRATION_GUIDE.md` around lines 378 - 394, Update the onCallback example
to either demonstrate using the new fourth parameter or explicitly state it's
optional: modify the async onCallback(error, context, session,
defaultOnCallback) example to call and await defaultOnCallback(error, context,
session) when you want the default behavior (e.g., fallback redirect) or show
how to wrap it (perform custom logic then return defaultOnCallback(...));
alternatively add a short sentence after the example clarifying that
defaultOnCallback is optional and older 3-parameter implementations (error,
context, session) remain supported.
🤖 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 `@src/server/auth-client.ts`:
- Around line 186-198: The popup branches call and await onCallback (the
developer hook) but discard its returned NextResponse, so cookies/headers set by
the hook are lost; update those branches in auth-client.ts to capture the
Promise<NextResponse> returned by onCallback (e.g., const hookResponse = await
onCallback(error, ctx, session, defaultOnCallbackPopup)) and then return that
hookResponse instead of ignoring it, and ensure you pass a popup-specific
default callback (e.g., defaultOnCallbackPopup) that builds the postMessage
response when invoking onCallback so the hook can override or augment it; apply
this change to the identified popup-handling branches that currently await
onCallback but do not return its value.

---

Nitpick comments:
In `@EXAMPLES.md`:
- Around line 2547-2567: Update the example Auth0Client onCallback signature to
include the fourth parameter defaultOnCallback and show delegating to it after
custom logic: in the Auth0Client instantiation's async onCallback(error,
context, session, defaultOnCallback) example, demonstrate checking error and
doing custom behavior (e.g., logging or redirecting to a custom error page) and
then calling await defaultOnCallback(error, context, session) to run the
built-in behavior (or return its NextResponse) when appropriate; reference
Auth0Client, onCallback, defaultOnCallback, NextResponse, context, session, and
error so readers can find the handler and see how to run the default flow after
custom work.

In `@V4_MIGRATION_GUIDE.md`:
- Around line 378-394: Update the onCallback example to either demonstrate using
the new fourth parameter or explicitly state it's optional: modify the async
onCallback(error, context, session, defaultOnCallback) example to call and await
defaultOnCallback(error, context, session) when you want the default behavior
(e.g., fallback redirect) or show how to wrap it (perform custom logic then
return defaultOnCallback(...)); alternatively add a short sentence after the
example clarifying that defaultOnCallback is optional and older 3-parameter
implementations (error, context, session) remain supported.
🪄 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: 520f2b1d-591e-42c0-96ed-8c5a9c9ee036

📥 Commits

Reviewing files that changed from the base of the PR and between f75779c and a5a60de.

📒 Files selected for processing (4)
  • EXAMPLES.md
  • V4_MIGRATION_GUIDE.md
  • src/server/auth-client.test.ts
  • src/server/auth-client.ts

Comment thread src/server/auth-client.ts
Comment on lines +186 to +198
* If you do not need custom routing, simply return the result of calling
* the fourth argument (`defaultOnCallback`) to invoke the SDK's default behaviour.
*
* @param error - The error returned from Auth0 or during token exchange.
* `null` when the callback completed successfully.
* @param ctx - Context about the transaction that initiated the auth flow
* (e.g. `returnTo` URL, `responseType`, `challengeMode`).
* @param session - The `SessionData` that will be persisted after a
* successful callback. `null` when `error` is present.
* @param defaultOnCallback - The SDK's built-in callback handler. Call this
* to keep the default redirect/error behaviour while still running your own
* side effects (e.g. syncing the user to your database, setting extra
* cookies). Signature: `(error, ctx) => Promise<NextResponse>`.

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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Popup callbacks still ignore the hook’s NextResponse.

In these branches, onCallback is awaited only for side effects and the returned response is discarded. That makes the new defaultOnCallback contract inaccurate for popup flows: cookies or headers added to the returned NextResponse never reach the client, and defaultOnCallback does not actually represent the popup flow’s default response. Consider threading a popup-specific default callback that builds the postMessage response and then returning the hook’s result from these branches.

Possible direction
- await this.onCallback(null, onCallbackCtx, mergedSession, this.defaultOnCallback.bind(this));
- const popupResponse = createAuthCompletePostMessageResponse({ ... });
- ...
- return popupResponse;
+ const defaultOnCallback = async () => {
+   const popupResponse = createAuthCompletePostMessageResponse({ ... });
+   ...
+   return popupResponse;
+ };
+
+ return this.onCallback(null, onCallbackCtx, mergedSession, defaultOnCallback);

Also applies to: 1269-1274, 1319-1324, 2190-2195

🤖 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 186 - 198, The popup branches call
and await onCallback (the developer hook) but discard its returned NextResponse,
so cookies/headers set by the hook are lost; update those branches in
auth-client.ts to capture the Promise<NextResponse> returned by onCallback
(e.g., const hookResponse = await onCallback(error, ctx, session,
defaultOnCallbackPopup)) and then return that hookResponse instead of ignoring
it, and ensure you pass a popup-specific default callback (e.g.,
defaultOnCallbackPopup) that builds the postMessage response when invoking
onCallback so the hook can override or augment it; apply this change to the
identified popup-handling branches that currently await onCallback but do not
return its value.

@Piyush-85 Piyush-85 closed this May 16, 2026
@Piyush-85

Copy link
Copy Markdown
Contributor Author

After reviewing this more carefully, we've decided not to move ahead with exposing defaultOnCallback as a
parameter. The default callback behaviour is intentionally simple, a redirect to returnTo on success and a
500 on error and users who need side effects can replicate that in a few lines, as shown in our existing
docs example.

The bigger concern is the popup flow: defaultOnCallback would produce a redirect response there, which is
the wrong behaviour for a postMessage flow. Making the contract accurate for popup would add meaningful
complexity for a convenience gain that doesn't justify it.

Closing this out, but appreciate the contribution.

@Piyush-85
Piyush-85 deleted the expose-default-oncallback branch July 21, 2026 11:36
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.

3 participants