Repository: auth0/nextjs-auth0
Pull request #302 (2021-02-18 12:19:23)

Updating main with a PR from beta
Should have merged #298 into `main`


Patch:
diff --git a/src/auth0-session/handlers/callback.ts b/src/auth0-session/handlers/callback.ts
index 832e875d..8328afbd 100644
--- a/src/auth0-session/handlers/callback.ts
+++ b/src/auth0-session/handlers/callback.ts
@@ -15,6 +15,8 @@ export type AfterCallback = (req: any, res: any, session: any, state: Record<str
 
 export type CallbackOptions = {
   afterCallback?: AfterCallback;
+
+  redirectUri?: string;
 };
 
 export type HandleCallback = (req: IncomingMessage, res: ServerResponse, options?: CallbackOptions) => Promise<void>;
@@ -27,8 +29,7 @@ export default function callbackHandlerFactory(
 ): HandleCallback {
   return async (req, res, options) => {
     const client = await getClient();
-
-    const redirectUri = getRedirectUri(config);
+    const redirectUri = options?.redirectUri || getRedirectUri(config);
 
     let expectedState;
     let tokenSet;
diff --git a/src/handlers/callback.ts b/src/handlers/callback.ts
index 5acf924c..fa963f76 100644
--- a/src/handlers/callback.ts
+++ b/src/handlers/callback.ts
@@ -70,6 +70,8 @@ export type AfterCallback = (
  */
 export type CallbackOptions = {
   afterCallback?: AfterCallback;
+
+  redirectUri?: string;
 };
 
 /**


Test patch:
diff --git a/tests/auth0-session/fixtures/server.ts b/tests/auth0-session/fixtures/server.ts
index 63d593cc..c38d904d 100644
--- a/tests/auth0-session/fixtures/server.ts
+++ b/tests/auth0-session/fixtures/server.ts
@@ -17,7 +17,8 @@ import {
   logoutHandler,
   callbackHandler,
   LoginOptions,
-  LogoutOptions
+  LogoutOptions,
+  CallbackOptions
 } from '../../../src/auth0-session';
 import wellKnown from './well-known.json';
 import { jwks } from './cert';
@@ -52,7 +53,7 @@ class TestSessionCache implements SessionCache {
 type Handlers = {
   handleLogin: (req: IncomingMessage, res: ServerResponse, opts?: LoginOptions) => Promise<void>;
   handleLogout: (req: IncomingMessage, res: ServerResponse, opts?: LogoutOptions) => Promise<void>;
-  handleCallback: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
+  handleCallback: (req: IncomingMessage, res: ServerResponse, opts?: CallbackOptions) => Promise<void>;
   handleSession: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
 };
 
@@ -103,7 +104,7 @@ const parseJson = (req: IncomingMessage, res: ServerResponse): Promise<IncomingM
 
 const requestListener = (
   handlers: Handlers,
-  { loginOptions, logoutOptions }: { loginOptions?: LoginOptions; logoutOptions?: LogoutOptions }
+  { callbackOptions, loginOptions, logoutOptions }: { callbackOptions?: CallbackOptions; loginOptions?: LoginOptions; logoutOptions?: LogoutOptions }
 ) => async (req: IncomingMessage, res: ServerResponse): Promise<void> => {
   const { pathname } = url.parse(req.url as string, true);
   const parsedReq = await parseJson(req, res);
@@ -115,7 +116,7 @@ const requestListener = (
       case '/logout':
         return await handlers.handleLogout(parsedReq, res, logoutOptions);
       case '/callback':
-        return await handlers.handleCallback(parsedReq, res);
+        return await handlers.handleCallback(parsedReq, res, callbackOptions);
       case '/session':
         return await handlers.handleSession(parsedReq, res);
       default:
@@ -133,12 +134,14 @@ let server: HttpServer | HttpsServer;
 export const setup = async (
   params: Omit<ConfigParameters, 'baseURL'>,
   {
+    callbackOptions,
     loginOptions,
     logoutOptions,
     customListener,
     https
   }: {
     https?: boolean;
+    callbackOptions?: CallbackOptions;
     loginOptions?: LoginOptions;
     logoutOptions?: LogoutOptions;
     customListener?: (req: IncomingMessage, res: ServerResponse) => void;
@@ -172,7 +175,7 @@ export const setup = async (
   const port = await new Promise((resolve) => server.listen(0, () => resolve((server.address() as AddressInfo).port)));
   const baseURL = `http${https ? 's' : ''}://localhost:${port}`;
 
-  listener = customListener || requestListener(createHandlers({ ...params, baseURL }), { loginOptions, logoutOptions });
+  listener = customListener || requestListener(createHandlers({ ...params, baseURL }), { callbackOptions, loginOptions, logoutOptions });
   return baseURL;
 };
 
diff --git a/tests/auth0-session/handlers/callback.test.ts b/tests/auth0-session/handlers/callback.test.ts
index e4ac69fa..c0b5a487 100644
--- a/tests/auth0-session/handlers/callback.test.ts
+++ b/tests/auth0-session/handlers/callback.test.ts
@@ -372,4 +372,22 @@ describe('callback', () => {
     expect(res.statusCode).toEqual(302);
     expect(res.headers.location).toEqual(baseURL);
   });
+
+  it('should accept custom runtime redirect over base url', async () => {
+    const redirectUri = 'http://messi:3000/api/auth/callback/runtime';
+    const baseURL = await setup(defaultConfig, { callbackOptions: { redirectUri } });
+    const state = encodeState({ foo: 'bar' });
+    const cookieJar = toSignedCookieJar( { state, nonce: '__test_nonce__' }, baseURL);
+    const { res } = await post(baseURL, '/callback', {
+      body: {
+        state: state,
+        id_token: makeIdToken()
+      },
+      cookieJar,
+      fullResponse: true
+    });
+
+    expect(res.statusCode).toEqual(302);
+    expect(res.headers.location).toEqual(baseURL);
+  });
 });