Repository: auth0/nextjs-auth0
Pull request #1212 (2023-05-18 08:39:48)

[SDK-4113] Lock down open ended auth route
### 📋 Changes

- Replace use of open ended dynamic routes cee36e17362aaa2e80587a35fcaa2eb9c0b56907
- 404 when auth handler has extra parts 1eb9cb87de09afdf1b0ee25258ba02659a4c42ff



Patch:
diff --git a/EXAMPLES.md b/EXAMPLES.md
index 6659b9d3..72ee7aef 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -27,7 +27,7 @@ AUTH0_CLIENT_ID='CLIENT_ID'
 AUTH0_CLIENT_SECRET='CLIENT_SECRET'
 ```
 
-Create a [dynamic API route handler](https://nextjs.org/docs/api-routes/dynamic-api-routes) at `/pages/api/auth/[...auth0].js`.
+Create a [dynamic API route handler](https://nextjs.org/docs/api-routes/dynamic-api-routes) at `/pages/api/auth/[auth0].js`.
 
 ```js
 import { handleAuth } from '@auth0/nextjs-auth0';
@@ -126,7 +126,7 @@ export default initAuth0({
 Now rather than using the named exports, you can use the instance methods directly.
 
 ```js
-// pages/api/auth/[...auth0].js
+// pages/api/auth/[auth0].js
 import auth0 from '../../../utils/auth0';
 
 // Use the instance method
@@ -141,7 +141,8 @@ export default auth0.handleAuth();
 import auth0 from '../../../utils/auth0';
 import { handleLogin } from '@auth0/nextjs-auth0';
 
-export default auth0.handleAuth({ // <= instance method
+export default auth0.handleAuth({
+  // <= instance method
   async login(req, res) {
     try {
       // `auth0.handleAuth` and `handleLogin` will be using separate instances
@@ -159,7 +160,7 @@ export default auth0.handleAuth({ // <= instance method
 Pass custom parameters to the auth handlers or add your own logging and error handling.
 
 ```js
-// pages/api/auth/[...auth0].js
+// pages/api/auth/[auth0].js
 import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
 import { myCustomLogger, myCustomErrorReporter } from '../utils';
 
@@ -192,7 +193,7 @@ export default handleAuth({
 
 ## Use custom auth urls
 
-Instead of (or in addition to) creating `/pages/api/auth/[...auth0].js` to handle all requests, you can create them individually at different urls.
+Instead of (or in addition to) creating `/pages/api/auth/[auth0].js` to handle all requests, you can create them individually at different urls.
 
 Eg for login:
 
@@ -352,7 +353,7 @@ export default auth0.withMiddlewareAuthRequired(async function middleware(req) {
 Get an access token by providing your API's audience and scopes. You can pass them directly to the `handlelogin` method, or use environment variables instead.
 
 ```js
-// pages/api/auth/[...auth0].js
+// pages/api/auth/[auth0].js
 import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
 
 export default handleAuth({
@@ -404,7 +405,7 @@ Pass a custom authorize parameter to the login handler in a custom route.
 If you are using the [New Universal Login Experience](https://auth0.com/docs/universal-login/new-experience) you can pass the `screen_hint` parameter.
 
 ```js
-// pages/api/auth/[...auth0].js
+// pages/api/auth/[auth0].js
 import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
 
 export default handleAuth({
diff --git a/README.md b/README.md
index 8bbe0f8f..d43185e1 100644
--- a/README.md
+++ b/README.md
@@ -89,9 +89,9 @@ Go to your Next.js application and create a [catch-all, dynamic API route handle
 
 - Create an `auth` directory under the `/pages/api/` directory.
 
-- Create a `[...auth0].js` file under the newly created `auth` directory.
+- Create a `[auth0].js` file under the newly created `auth` directory.
 
-The path to your dynamic API route file would be `/pages/api/auth/[...auth0].js`. Populate that file as follows:
+The path to your dynamic API route file would be `/pages/api/auth/[auth0].js`. Populate that file as follows:
 
 ```js
 import { handleAuth } from '@auth0/nextjs-auth0';
diff --git a/src/config.ts b/src/config.ts
index 81eda46a..fa339eb1 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -372,7 +372,7 @@ export interface NextConfig extends Pick<BaseConfig, 'identityClaimFilter'> {
  * {@link WithApiAuthRequired}, and {@link WithPageAuthRequired}).
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth } from '@auth0/nextjs-auth0';
  *
  * return handleAuth();
@@ -435,7 +435,7 @@ export interface NextConfig extends Pick<BaseConfig, 'identityClaimFilter'> {
  * Then import it into your route handler:
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import auth0 from '../../../../utils/auth0';
  *
  * return auth0.handleAuth();
diff --git a/src/handlers/auth.ts b/src/handlers/auth.ts
index 8e62c51a..c3291e2b 100644
--- a/src/handlers/auth.ts
+++ b/src/handlers/auth.ts
@@ -10,7 +10,7 @@ import { HandlerError } from '../utils/errors';
  * `login`, `logout`, `callback`, and `profile`. For example:
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
  * import { errorReporter, logger } from '../../../utils';
  *
@@ -33,7 +33,7 @@ import { HandlerError } from '../utils/errors';
  * Alternatively, you can customize the default handlers without overriding them. For example:
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({
@@ -46,7 +46,7 @@ import { HandlerError } from '../utils/errors';
  * You can also create new handlers by customizing the default ones. For example:
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({
@@ -72,10 +72,10 @@ type ErrorHandlers = {
  * The main way to use the server SDK.
  *
  * Simply set the environment variables per {@link ConfigParameters} then create the file
- * `pages/api/auth/[...auth0].js`. For example:
+ * `pages/api/auth/[auth0].js`. For example:
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth();
@@ -165,7 +165,14 @@ export default function handlerFactory({
         query: { auth0: route }
       } = req;
 
-      route = Array.isArray(route) ? route[0] : /* c8 ignore next */ route;
+      if (Array.isArray(route)) {
+        let otherRoutes;
+        [route, ...otherRoutes] = route;
+        if (otherRoutes.length) {
+          res.status(404).end();
+          return;
+        }
+      }
 
       try {
         const handler = route && customHandlers.hasOwnProperty(route) && customHandlers[route];
diff --git a/src/handlers/callback.ts b/src/handlers/callback.ts
index 9bf41240..ac7bc7f7 100644
--- a/src/handlers/callback.ts
+++ b/src/handlers/callback.ts
@@ -14,7 +14,7 @@ import { CallbackHandlerError, HandlerErrorCause } from '../utils/errors';
  * @example Validate additional claims
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';
  *
  * const afterCallback = (req, res, session, state) => {
@@ -39,7 +39,7 @@ import { CallbackHandlerError, HandlerErrorCause } from '../utils/errors';
  * @example Modify the session after login
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';
  *
  * const afterCallback = (req, res, session, state) => {
@@ -62,7 +62,7 @@ import { CallbackHandlerError, HandlerErrorCause } from '../utils/errors';
  * @example Redirect successful login based on claim
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';
  *
  * const afterCallback = (req, res, session, state) => {
@@ -138,7 +138,7 @@ export type CallbackOptionsProvider = (req: NextApiRequest) => CallbackOptions;
  * @example Pass an options object
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({
@@ -149,7 +149,7 @@ export type CallbackOptionsProvider = (req: NextApiRequest) => CallbackOptions;
  * @example Pass a function that receives the request and returns an options object
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({
diff --git a/src/handlers/login.ts b/src/handlers/login.ts
index 5d1bbc99..243e0094 100644
--- a/src/handlers/login.ts
+++ b/src/handlers/login.ts
@@ -10,7 +10,7 @@ import { HandlerErrorCause, LoginHandlerError } from '../utils/errors';
  * Use this to store additional state for the user before they visit the identity provider to log in.
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
  *
  * const getLoginState = (req, loginOptions) => {
@@ -173,7 +173,7 @@ export type LoginOptionsProvider = (req: NextApiRequest) => LoginOptions;
  * @example Pass an options object
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({
@@ -186,7 +186,7 @@ export type LoginOptionsProvider = (req: NextApiRequest) => LoginOptions;
  * @example Pass a function that receives the request and returns an options object
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({
diff --git a/src/handlers/logout.ts b/src/handlers/logout.ts
index 309d905f..7239f823 100644
--- a/src/handlers/logout.ts
+++ b/src/handlers/logout.ts
@@ -39,7 +39,7 @@ export type LogoutOptionsProvider = (req: NextApiRequest) => LogoutOptions;
  * @example Pass an options object
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleLogout } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({
@@ -50,7 +50,7 @@ export type LogoutOptionsProvider = (req: NextApiRequest) => LogoutOptions;
  * @example Pass a function that receives the request and returns an options object
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleLogout } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({
diff --git a/src/handlers/profile.ts b/src/handlers/profile.ts
index eba12ba8..f915615b 100644
--- a/src/handlers/profile.ts
+++ b/src/handlers/profile.ts
@@ -44,7 +44,7 @@ export type ProfileOptionsProvider = (req: NextApiRequest) => ProfileOptions;
  * @example Pass an options object
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({
@@ -55,7 +55,7 @@ export type ProfileOptionsProvider = (req: NextApiRequest) => ProfileOptions;
  * @example Pass a function that receives the request and returns an options object
  *
  * ```js
- * // pages/api/auth/[...auth0].js
+ * // pages/api/auth/[auth0].js
  * import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';
  *
  * export default handleAuth({


Test patch:
diff --git a/tests/handlers/auth.test.ts b/tests/handlers/auth.test.ts
index b867fcd2..7827bb9c 100644
--- a/tests/handlers/auth.test.ts
+++ b/tests/handlers/auth.test.ts
@@ -50,6 +50,14 @@ describe('auth handler', () => {
     global.handleAuth = initAuth0(withoutApi).handleAuth;
     await expect(get(baseUrl, '/api/auth/401')).rejects.toThrow('Unauthorized');
   });
+
+  test('return 404 when routes have extra parts', async () => {
+    const baseUrl = await setup(withoutApi);
+    global.handleAuth = initAuth0(withoutApi).handleAuth;
+    await expect(get(baseUrl, '/api/auth/me.css')).rejects.toThrow('Not Found');
+    await expect(get(baseUrl, '/api/auth/me/foo.css')).rejects.toThrow('Not Found');
+    await expect(get(baseUrl, '/api/auth/me/foo/bar.css')).rejects.toThrow('Not Found');
+  });
 });
 
 describe('custom error handler', () => {